With the given information regarding the customers, we can predict which bidding strategies will yield the highest lifetime revenues for the least amount of money through data analysis and exploration and predict the CLV of a given customer.
Using Watson Analytics data, we can predict customer behavior to retain customers. We can analyze all relevant customer data and develop focused customer retention programs.The question that we are trying to solve is to discover what affects customer engagement and to provide actionable recommendations for the businessBusiness strategy should be to Acquire more customers + to retain more customers = To increase customer profitability.
In this project, We have tried to find the effect of different variables on the target variable through visualizations, statistical tests and statistical models and compared the results.
Exploratory Data Analysis
To identify quantity and percentage of zeros
status(data)
From here, we can observe that there are lot of people whose income is 0 since they are unemployed.
Our objective here is to visualize the given data and look for variables that can be important for modelling.
Customer Lifetime Value
This is our target variable.
fig_CLV <- plot_ly(x =data$`Customer Lifetime Value`, type = "histogram")%>% layout(title =" CLV DISTRIBUTION IN THE DATA")
fig_CLV
NA
NA
Customer lifetime value is positively skewed.
Response
ggplot(data,aes(Response))+geom_bar(fill="pink",col="black",width=0.7,position=position_dodge(0.9))+
geom_text(stat="count",aes(label = after_stat(count)),vjust=2)+
theme(
text=element_text(size=10),
axis.title.x = element_text(color="black", size=15),
axis.title.y = element_text(color="black", size=15)
)

NA
NA
ggplot(data, aes(x =Response, y = `Customer Lifetime Value`)) +
geom_boxplot() +
xlab("Response of customers") + ylab("CLV")

Here, we can observe that very few people have reapplied for policy. So, if our focus is to retain customers we should target customers who said NO as those customers who have said no are of high customer lifetime value.
Coverage
ggplot(data, aes(x =Coverage, y = `Customer Lifetime Value`)) +
geom_boxplot() +
xlab("Coverage of customers") + ylab("CLV")

aggregate( `Customer Lifetime Value` ~ Coverage, data, mean)
This can be an important variable, as different groups show major differences in their value, and which kind of coverage they are choosing may help in deciding their customer life time value.
Education
ggplot(data, aes(x =Education, y = `Customer Lifetime Value`)) +
geom_boxplot() +
xlab("Education of customers") + ylab("CLV")

Doctor in general have low customer life time value, while Masters and High School or Below have higher customer life time value. But it’s still tough to observe any major changes across different groups. So, it may be possible that this variable is not important.
Employment Status
stripplot(~`Customer Lifetime Value`|EmploymentStatus,data,
panel=function(x,y,...) {
m=median(x)
panel.stripplot(x,y,...)
panel.stripplot(m,y,pch="|",cex=2,col=2)
}
)

ggplot(data, aes(x =EmploymentStatus, y = `Customer Lifetime Value`)) +
geom_boxplot() +
xlab("EmploymentStatus of customers") + ylab("CLV")

Customer with high customer lifetime values lies mainly in Employed and unemployed categories, while it appears that there isn’t any major difference across categories. Also, we can say that people who are Retired, on Medical leave, Disabled their customer lifetime value is less. Employed people’s customer lifetime value definitely turns out to be highest among all. There is one data point in disabled which can be treated as outlier as it’s value is really high, which doesn’t make it a general case. So, we remove that data point.
subset(data,EmploymentStatus=='Disabled'& "Customer Lifetime Value">40000)
NA
data=data[data$Customer !='XF89906',]
This row is dropped.
Gender
ggplot(data,aes(Gender))+geom_bar(fill="grey",col="black",width=0.7,position=position_dodge(0.9))+
geom_text(stat="count",aes(label = after_stat(count)),vjust=2)+
theme(
text=element_text(size=10),
axis.title.x = element_text(color="black", size=15),
axis.title.y = element_text(color="black", size=15)
)

ggplot(data, aes(x =Gender, y = `Customer Lifetime Value`)) +
geom_boxplot() +
xlab("Gender") + ylab("CLV")

It appears that gender has no effect on customer lifetime value.
Location Code
ggplot(data,aes(`Location Code`))+geom_bar(fill="grey",col="black",width=0.7,position=position_dodge(0.9))+
geom_text(stat="count",aes(label = after_stat(count)),vjust=2)+
theme(
text=element_text(size=10),
axis.title.x = element_text(color="black", size=15),
axis.title.y = element_text(color="black", size=15)
)

aggregate( `Customer Lifetime Value` ~ `Location Code`, data, mean)
We should focus more on suburbans. as they are our major customers We can’t say any major difference between these groups.We get same inference using boxplots as well.
Vehicle Class
ggplot(data, aes(x =`Vehicle Class`, y = `Customer Lifetime Value`)) +
geom_boxplot() +
xlab("Vehicle Class of customers") + ylab("CLV")

aggregate( `Customer Lifetime Value` ~ `Vehicle Class`, data, mean)
head(data)
Luxury Car,Luxury SUV have high customer lifetime value, while Four-Door Car and Two-Door Car have less customer lifetime values. So, this variable may be important for prediction as it varies across different categories.
Sales Channel
head(data)
ggplot(data, aes(x =`Sales Channel`, y = `Customer Lifetime Value`)) +
geom_boxplot() +
xlab("Sales Channel of customers")

Not much difference is observed across different categories, but we can see presence of an outlier. So, we will treat it.
head(data)
subset(data,`Sales Channel`=='Call Center' & 'Customer Lifetime Value'>50000)
data=data[data$Customer !='FQ61281',]
Renew Offer Type
ggplot(data, aes(x =`Renew Offer Type`, y =`Customer Lifetime Value`)) +
geom_boxplot() +
xlab("Renew Offer Type of customers")

aggregate( `Customer Lifetime Value` ~ `Renew Offer Type`, data, mean)
Offer 1 and Offer 3 seems to represent little higher valued customers. But we can’t say if difference across categories is significant enough.
Policy Type
ggplot(data, aes(x =`Policy Type`,y =`Customer Lifetime Value`)) +
geom_boxplot() +
xlab("Policy type of customers") + ylab("CLV")

aggregate( `Customer Lifetime Value` ~ `Policy Type`, data, mean)
Special Auto range of customer life time values is in general higher than others, while Personal Auto policy type is the one that is generally opted by customers.
Binning Columns
# Add Income bins as new column
data$"Income Bin" <- cut(data$"Income",
breaks = c(-1,14999,29999,44999,59999,74999,Inf),
labels = c("< $15000", "$15000-29999","$30000-44999",
"$45000-59999", "$60000-74999", "$75000+"))
# Add Monthly Premium bins as new column
data$"Monthly Premium Bin" <- cut(data$"Monthly Premium Auto",
breaks = c(0,74,99,124,149,Inf),
labels = c("< $75", "$75-99","$100-124","$125-149","$150+"))
# Add Total Claim bins as new column
data$"Total Claim Bin" <- cut(data$"Total Claim Amount",
breaks = c(0,249,499,749,999,Inf),
labels = c("< $250", "$250-499","$500-749","$750-999","$1000+"))
ggplot(data, aes(x =`Monthly Premium Bin`, y = `Customer Lifetime Value`)) +
geom_boxplot() +
xlab("Monthly Premium Bin of customers") + ylab("CLV")

aggregate( `Customer Lifetime Value` ~ `Monthly Premium Bin`, data, mean)
ggplot(data, aes(x =`Total Claim Bin`, y = `Customer Lifetime Value`)) +
geom_boxplot() +
xlab("Claim bin of customers") + ylab("CLV")

aggregate( `Customer Lifetime Value` ~ `Total Claim Bin`, data, mean)
Creating bins for income, doesn’t show any changes across bins. But for Monthly Premium Bin and claim bin we can observe changes across categories.
Is there any preferable policy for very high valued customers?
subset(data,`Customer Lifetime Value`>50000)
Personal Auto policy type is the preferable policy for very high lifetime value customers.
Are agents more effective with regards to purchase of policy plans?
df_1=subset(data, (Response=="Yes") )
aggregate(Customer ~ `Sales Channel`,df_1, FUN = length)
aggregate(Customer ~ `Sales Channel`,data, FUN = length)
0.19%,0.11%,0.11%,0.12% are the respective percentages of people who responded with yes from different sales channel w.r.t total customers each channel brought.This shows that people are more likely to respond to agents rather than other sales channel mode.
Can we use knowledge of gender as leverage in any case?
subset(data, (Response=="Yes")& (`Customer Lifetime Value`>25000))
NA
After observing graphs it appeared that gender has no role to play, but we observe that all high valued customers who gave yes as response for policy renewal are females. So, we should focus more on high valued female customers who gave no as response, as they have more chance to say yes. People prefer to take 2 policies in general, so we can use it to our leverage.
Based on EDA, which variables are not important for prediction?
Customer,State, Marital Status, Education,Gender,Location code,Sales Channel,Renew offer type,Policy,Months Since Policy Inception, Months Since Last Claim,Income don’t come across as important variables from our data exploration.Monthly Premium Auto is collinear with Total Claim Amount, so we need to drop one of these variables.
Based on EDA, which variables are important for prediction?
Response,Coverage,EmploymentStatus,Vehicle class,Policy type,Total Claim Amount appear to be important variables.
Multivariate Analysis
#install.packages("RColorBrewer")
library(RColorBrewer)
CLV_Type <- ggplot(data, aes(x=`Number of Policies`, y=`Customer Lifetime Value`, fill = `Sales Channel` ))+
geom_col(position="dodge") + xlab("NUMBER OF POLICIES") + ylab("Customer Lifetime Value") +
ggtitle("Customer Lifetime Value by Sales Channel and Policy") +
scale_fill_brewer(palette = "Paired")
CLV_Type

NA
NA
The Average number of policies that the Company issues comes to be around 2-3 in the the given time frame and it is noticeable that Call Centers fetch the most valuable customer.It is also to be noted that Customers having higher number of policies directly approach the Branch.Here the Policies to be issued vs the Lifetime Value of a Customer could be a trade off, since our focus is Lifetime Value we should be focusing on attracting more customers through Call centers.
Claim_Type <- ggplot(data, aes(x=`Number of Policies`, y=data$`Total Claim Amount`, fill =`Policy Type` ))+
geom_col(position="dodge") + xlab("NUMBER OF POLICIES") + ylab("Total Claim Amt") +
ggtitle("Total Claim by no.of policy and Policy type") +
scale_fill_brewer(palette = "Paired")
Claim_Type

It is evident that our major share of Customers procure Personal insurance and since the Total Claim Value determines the Lifetime Value of a customer the No.of Policy sold per category plays a crucial role to generate value to the company.
loc<-count(data,`Location Code`,Coverage)
#View(loc)
ggplot(data = data, aes(x = `Location Code` , y =`Monthly Premium Auto` , color = Coverage)) +
geom_boxplot() +
xlab("Location") + ylab("Monthly Premium")

This to strategize the Plan to be promoted in different Locations, since we know most of our Customers come from Sub-Urban locations, where the Premium coverage numbers are high it is also seen that Premium coverage is preferred by most irrespective of the location.Hence prioritizing Premium Customers can benefit the business.

No.of Complaints can reflect upon relationship of the Customer with the Company given that the services are not managed well by the company.Most of our policies are for Personal purpose and are marketed by Call-Center.Personal policies being our focal point we see that those policies distributed by Agents have major number of Complaints which indicates poor-job by the Agents and delayed response by the company to the Target customers.
Modelling
Linear Regression
Linear Regression is the oldest, simple and widely used supervised machine learning algorithm for predictive analysis. It is a method to predict a target variable(Y) by fitting the best linear relationship between the dependent(Y) and independent variable(X). It helps determine:
If a independent variable does a good job in predicting the dependent variable.
Which independent variable plays a significant role in predicting the dependent variable.
In our analysis, we identified the target variable to be predicted as Customer Lifetime Value and all the others as dependent variable to run a multiple regression model.
Factorisation
Factor in R is a variable used to categorize and store the data, having a limited number of different values. It stores the data as a vector of integer values.
We converted all the categorical variables and 2 discrete numeric columns(Number of policies and number of open complaints) into factors to give each category a level to help the regression analysis.
Train-Test Split
The train-test split is a technique for evaluating the performance of the model
Train Dataset: Used to fit the machine learning model.
Test Dataset: Used to evaluate the fit machine learning model.
The objective is to estimate the performance of the model on new data: data not used to train the model.
Here, the data is randomly split in the ratio of 7:3 where training data constitutes 70% of the data and testing data is 30% of the complete dataset.
#LR TILL TRAIN TEST SPLIT
data$`Customer Lifetime Value`=log(data$`Customer Lifetime Value`)
set.seed(123)
sample <- sample(c(TRUE, FALSE), nrow(data), replace = T, prob = c(0.7,0.3))
train <- data[sample, ]
test <- data[!sample, ]
Approach
We initially built a base model with all the variables and got R^2 value of approximately 0.64. When we constructed a graph of Residual v/s Fitted the graph was funnel shaped indicating heteroscedasticity which is against the linear regression assumptions. This was due to the skewness in the target variable. Hence we log transformed the target variable.
Using the log transformed target column when we built a model, the score jumped up to 0.896. But the number of dependent variables used were 22. [However, the customer id column was initially dropped as it is of no importance] In order to optimize the usage of 22 dependent variables, we did various trial and error methods to get maximum information from minimum columns.
Statistical tests like correlation, Kruskal Wallis tests were also performed to see the dependence of each independent variable for the prediction of CLV.
However, we finally came up with the efficient model using anova for feature importance.
Feature Selection
The main aim of a regression analysis is to fetch as much information as possible with the least number of variables which are significant.To achieve this we need to select the most significant independent varibles that can explain the variation of the dependent variable. Feature selection was performed using a statistical test called ANOVA. ANOVA is a statistical test for estimating how a quantitative dependent variable changes according to the levels of one or more categorical independent variables
#ANOVA
#install.packages('AICcmodavg')
#library(AICcmodavg)
anova <- aov(`Customer Lifetime Value` ~
State+Response+Coverage +
Education+`Effective To Date` +EmploymentStatus+Gender+
Income+`Location Code`+`Months Since Policy Inception`+
`Marital Status`+ `Months Since Last Claim`+`Policy Type`+
Policy+`Sales Channel`+
`Renew Offer Type` +`Vehicle Size`+
`Monthly Premium Auto`+
`Number of Open Complaints`+
`Number of Policies`+
`Total Claim Amount`+
`Vehicle Class`, data = data)
summary(anova)
Df Sum Sq Mean Sq F value Pr(>F)
State 4 1.3 0.3 7.404 5.99e-06 ***
Response 1 0.2 0.2 3.610 0.0575 .
Coverage 2 195.2 97.6 2228.672 < 2e-16 ***
Education 4 4.0 1.0 22.599 < 2e-16 ***
`Effective To Date` 1 0.3 0.3 5.766 0.0164 *
EmploymentStatus 4 13.1 3.3 74.957 < 2e-16 ***
Gender 1 0.2 0.2 3.944 0.0471 *
Income 1 0.0 0.0 0.876 0.3492
`Location Code` 2 0.4 0.2 4.142 0.0159 *
`Months Since Policy Inception` 1 0.0 0.0 0.003 0.9586
`Marital Status` 2 2.6 1.3 29.332 2.01e-13 ***
`Months Since Last Claim` 1 0.2 0.2 4.094 0.0431 *
`Policy Type` 2 2.1 1.0 23.654 5.67e-11 ***
Policy 6 2.2 0.4 8.216 6.88e-09 ***
`Sales Channel` 3 1.2 0.4 9.462 3.08e-06 ***
`Renew Offer Type` 3 72.0 24.0 548.299 < 2e-16 ***
`Vehicle Size` 2 3.2 1.6 36.309 < 2e-16 ***
`Monthly Premium Auto` 1 545.7 545.7 12461.628 < 2e-16 ***
`Number of Open Complaints` 5 10.2 2.0 46.387 < 2e-16 ***
`Number of Policies` 8 2619.8 327.5 7478.553 < 2e-16 ***
`Total Claim Amount` 1 0.1 0.1 1.901 0.1680
`Vehicle Class` 5 22.4 4.5 102.221 < 2e-16 ***
Residuals 9073 397.3 0.0
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
From the results obtatined from ANOVA we selected 9 variables to be significant with the Customer Lifetime Value variable based on their p values. All the variables significant at 0.1 level of significance and below were considered for building the model. Namely, Coverage,Education, Effective To Date, EmploymentStatus, Policy,Renew Offer Type,Monthly Premium Auto,Vehicle Class,Number of Open Complaints, Number of Policies. However we considered Policy over Policy type as they both explain similar things. We also neglected Effective To Date column as the results did not affect much with it. Also, we have not used the derived column “Present Value of Customer” as only Monthly Premium Auto was considered significant whereas other variables used weren’t. The R^2 value dropped when the derived column was used.
Model Building
Using the important variables obtained from ANOVA we trained a linear regression model using lm function on the training data.
Accuracy Measures
R^2 value- It represents the proportion of variance explained and always takes on a value between 0 and 1. The higher the value, better the model. It is independent of the scale of Y.
RMSE - Root Mean Square Error (RMSE) is the standard deviation of the residuals (prediction errors). Residuals are a measure of how far from the regression line data points are. RMSE is a measure of how spread out these residuals are. In other words, it tells you how concentrated the data is around the line of best fit.
result = broom::glance(fit_log)
result
NA
NA
NA
#install.packages("modelr")
library(modelr)
#install.packages("broom")# provides easy pipeline modeling functions
library(broom)
Attaching package: ‘broom’
The following object is masked from ‘package:modelr’:
bootstrap
#install.packages("Metrics")
library(Metrics)
Attaching package: ‘Metrics’
The following objects are masked from ‘package:modelr’:
mae, mape, mse, rmse
The following objects are masked from ‘package:caret’:
precision, recall
library(dplyr)
#install.packages("pbkrtest", dependencies = TRUE)
#library(caret)
test %>%
add_predictions(fit_log)
#summarise(MSE = mean((`Customer Lifetime Value`-pred)^2))
y_train = predict(fit_log, newdata = train)
R2 <- 1- (sum((train$`Customer Lifetime Value`-y_train)^2) / sum((train$`Customer Lifetime Value` - mean(train$`Customer Lifetime Value`))^2))
print(R2 * 100)
[1] 89.59918
caret::RMSE(y_train,train$`Customer Lifetime Value`)
[1] 0.2099365
y_test = predict(fit_log, newdata = test)
R3 <- 1- (sum((test$`Customer Lifetime Value`-y_test)^2) / sum((test$`Customer Lifetime Value` - mean(test$`Customer Lifetime Value`))^2))
print(R3 * 100)
[1] 89.82091
caret::RMSE(y_test,test$`Customer Lifetime Value`)
[1] 0.2096528
After training the model , we can get a summary of results using broom package which gives r^2, adj r^2, RSE and other accuracy measures to asses the model performace.
However, we assessed the training and testing predictions by calculting the R^ value and RMSE for training and testing data.
The results obtained are: train data : R^2=0.8959482 and RMSE=0.2099805 test data : R^2=0.8982095 and RMSE=0.2096523
The obtained results were satisfactory with less error and hence concluded this to be the best fit model for prediction of the target variable Customer Lifetime Value from the most significant variables.
Assumptions and Residual Plots for Accuracy Measure
Correlation of Error Terms
data %>%
add_residuals(fit_log) %>%
ggplot(aes(data$`Customer Lifetime Value`, resid)) +
geom_line()

Correlation of error term An important assumption of the linear regression model is that the error terms are uncorrelated. This scatterplot is used to detect a particular form of non-independence of the error terms, namely serial correlation. A Residual vs. order plot helps to see if there is any correlation between the error terms that are near each other in the sequence.However, if we look at our model’s residuals we see that adjacent residuals do not tend to take on similar values, hence error terms are not correlated.
Detecting Multicollinearity using GVIF
car::vif(fit_log)
GVIF Df GVIF^(1/(2*Df))
Coverage 6.080094 2 1.570282
Education 1.050078 4 1.006127
EmploymentStatus 1.101621 4 1.012171
Policy 1.038022 8 1.002335
`Renew Offer Type` 1.131443 3 1.020796
`Monthly Premium Auto` 25.656367 1 5.065211
`Number of Open Complaints` 1.049274 5 1.004821
`Number of Policies` 1.070495 8 1.004267
`Vehicle Class` 20.693570 5 1.353891
Multicollinearity can be assessed by computing the VIF(variance inflation factor) value. Any variable with a high VIF value should be removed from the model. In our model all the variables have VIF values between 1 and 5 which satisfies the condition, means no severity of multicollinearity. Here gvif is the square root of the VIF for individual predictors and thus can be used equivalently
Residual Plots
plot(fit_log)




Residual Vs Fitted Values In this scatter plot, the distribution of residuals (errors) vs fitted values (predicted values) is depicted.Since the plot now does not show a funnel shape, it is an indication of constant variance, i.e.homoskedasticity. Also, since there is no recognizable pattern seen, it indicates that the assumption of linearity is fair.
Normal Q-Q Plot This q-q, or quantile-quantile, scatter plot helps in the validation of the normal distribution assumption in our data set. We can infer if the data has a normal distribution by looking at this graph. If this is the case, the plot will tend to be fairly straight line. In our case, there is strong deviation from the diagonal line which shows that our residuals are not normally distributed.
Scale-Location Plot This plot can be used to detect homoskedasticity (assumption of equal variance). It displays how the residuals are distributed across the predictor range. It’s similar to the residual vs. fitted value plot, but it actually uses standardised residual values. Here, we can see a diagonal line with somewhat equally distributed points which shows less homoskedasticity.
Residuals Vs Leverage Plot This is also known as Cook’s Distance plot. It is a method of determining which points have more influence than others. Such influential locations have a significant impact on the regression line. In our case, Cook’s distance scores are high and are clustered near the top of our leverage plot, indicating that they have a significant influence on the regression results.
Random Forest
#install.packages("randomForest")
library(randomForest)
Warning: package ‘randomForest’ was built under R version 4.0.5
randomForest 4.6-14
Type rfNews() to see new features/changes/bug fixes.
Attaching package: ‘randomForest’
The following object is masked from ‘package:gridExtra’:
combine
The following object is masked from ‘package:ggplot2’:
margin
The following object is masked from ‘package:dplyr’:
combine
#head(train)
#colnames(train)
RF_fit<- randomForest(`Customer Lifetime Value` ~
Coverage+
Education+
EmploymentStatus+
train$`Policy` +
train$`Renew Offer Type`+
train$`Monthly Premium Auto`+
train$`Number of Open Complaints`+
train$`Number of Policies`+
train$`Vehicle Class`,
data=train)
RF_fit
Call:
randomForest(formula = `Customer Lifetime Value` ~ Coverage + Education + EmploymentStatus + train$Policy + train$`Renew Offer Type` + train$`Monthly Premium Auto` + train$`Number of Open Complaints` + train$`Number of Policies` + train$`Vehicle Class`, data = train)
Type of random forest: regression
Number of trees: 500
No. of variables tried at each split: 3
Mean of squared residuals: 0.04106598
% Var explained: 90.31
#caret::RMSE(y_test,test$`Customer Lifetime Value`)
From the summary results of the predicted values, when the Random Forest Regressor is tasked with the problem of predicting for values not previously seen, it will always predict an average of the values seen previously. Obviously the average of a sample can not fall outside the highest and lowest values in the sample. The Random Forest Regressor is unable to discover trends that would enable it in extrapolating values that fall outside the training set. When faced with such a scenario, the regressor assumes that the prediction will fall close to the maximum value in the training set.
The obtained train score is 95.73442 and test score is 89.81142 for Random Forest. This show that there is a slight overfitting of the model.
The complexity of Random Forest model is high as it is based on bootstrap aggregation and bagging techniques.
Support Vector Machine
#SVM
#install.packages("e1071")
library(e1071)
Warning: package ‘e1071’ was built under R version 4.0.5
Attaching package: ‘e1071’
The following object is masked from ‘package:Hmisc’:
impute
fit<- svm(data$`Customer Lifetime Value` ~
data$Education+data$`Effective To Date` +data$EmploymentStatus+data$Gender+
data$Income+data$`Location Code` +data$`Months Since Policy Inception`+
data$`Marital Status`+ data$`Months Since Last Claim`+data$`Policy Type`+
data$`Monthly Premium Auto` + data$Policy +data$`Sales Channel`+
data$`Number of Open Complaints` + data$`Number of Policies` +
data$`Renew Offer Type` + data$`Total Claim Amount`+
data$`Vehicle Class`+data$`Vehicle Size`, data=train)
summary(fit)
Call:
svm(formula = data$`Customer Lifetime Value` ~ data$Education + data$`Effective To Date` +
data$EmploymentStatus + data$Gender + data$Income + data$`Location Code` + data$`Months Since Policy Inception` +
data$`Marital Status` + data$`Months Since Last Claim` + data$`Policy Type` + data$`Monthly Premium Auto` +
data$Policy + data$`Sales Channel` + data$`Number of Open Complaints` + data$`Number of Policies` +
data$`Renew Offer Type` + data$`Total Claim Amount` + data$`Vehicle Class` + data$`Vehicle Size`,
data = train)
Parameters:
SVM-Type: eps-regression
SVM-Kernel: radial
cost: 1
gamma: 0.01785714
epsilon: 0.1
Number of Support Vectors: 2853
predictedY_ <- predict(fit, train)
error_2 <- train$"Customer Lifetime Value" - predictedY_
svm_error <- sqrt(mean(error_2^2))
svm_error
[1] 0.8497346
predictedY <- predict(fit, test)
error_2 <- test$`Customer Lifetime Value` - predictedY
svm_error <- sqrt(mean(error_2^2))
svm_error
[1] 0.8459781
On applying SVM model, we get 7900 RMSE which is much higher than the basic model of Linear regression which gave us approx 4000 RMSE . These RMSE’s have been compared without log transformation, and if we want better prediction from SVM we need to optimize the model in a better way.
Results of important variables from EDA, ANOVA and Linear Regression
- EDA
Response,Total Claim Amount,Coverage,EmploymentStatus,Vehicle class,Policy type, Monthly Premium Auto [Due multicollinearity b/w Total Claim Amount and Monthly Premium Auto, rejected Total Claim Amount to be an important variable.]
- ANOVA
Coverage,Education,EmploymentStatus, Policy,Renew Offer Type,Monthly Premium Auto,Vehicle Class,Number of Open Complaints,Number of Policies
- LINEAR REGRESSION
Education,Employment Status, Gender, Monthly premium Auto, Sales Channel, Number of open complaints, Number of policieS, Vehicle Class
Conclusion
Why CLV?
Ultimately, the company just needs to be mindful of the value that a customer provides over their lifetime relationship with it. By understanding the customers’ details regarding various aspects and analyzing all key touchpoints, one can understand the key drivers of CLV. CLV is indeed a great metric that should be used to improve business strategies.
How good is the analysis?
Performing Exploratory Data Analysis, Statistical tests and using Statistical models like linear regression we analysed the effect of different variables on the variation of the target variable - Customer Lifetime Value and also concluded which are the important variables that are sufficient to give maximum information to predict the CLV. The results obtained from all the 3 methods were approximately similar and hence the analysis seems to be efficient.
How good is the final model?
Considering number of factors, we can conclude that Linear Regression model, being one of the simplest models has given the best R^2 value of 0.89 with least error rate of 0.20 compared to other models like random forest and Support Vector Machine which has its own cons over Linear regression. We have optimized the model to predict the Customer Lifetime Value from 9 independent and hence can be concluded as a pretty good model for prediction.
Business recommendations
From the model and the analysis we designed, we can suggest that:
The company should target mainly the customers who are employed.
The customers whose Education is master level should be targeted more whereas doctors do not serve to be valuable customers.
The number of complaints should be reduced because more number of complaints the company is more prone towards losing the customer. Compalints above 2 should focused.
More attention should be given to the Extended and premium customers.
The target audience should be female as they are easier to convince according to our analysis of response given towards the policy.
The company should start increasing their policy advertisement through branch and agents as the number of policy affects the CLV.
Suggestions
Suggesting new variables that can improve the analysis: 1. Debt to income ratio 2. Number of houses owned 3. Number of cars owned 4. Type of purchase- Installment or one-off 5. Price of insured commodity
Contribution:
Dona Sam (20BDA02) - EDA + Assumptions of Linear Regression
Prathibha K S (20BDA15) - EDA + Linear Regression + Random Forest + Report writing
Reba Susan Joseph (20BDA37) - EDA + Assumptions of Linear Regression
Jayasree C (20BDA53) - EDA + Compiling for the final report + Report Writing
Abhijith P K (20BDA60) - EDA + Statistical Tests
Ananya Kumari (20BDA68) - Compiling and deriving insights from EDA + SVM + Stepwise Regression + Report Writing
References:
https://medium.com/@aritraadhikari.b3/predicting-customer-lifetime-value-for-an-auto-insurance-company-3b24d8bf4e24
https://mediaalpha.com/article/why-auto-insurance-advertisers-should-optimize-for-customer-lifetime-value/
https://github.com/abhiyerasi/CLV-Auto-Insurance/blob/master/Code/Clustering.R
https://www.kaggle.com/dktalaicha/predict-customer-life-time-value-clv#1.-Objective-:--Predict-Customer-Life-time-Value-(CLV-)for-an-Auto-Insurance-Company.
https://www.kaggle.com/juancarlosventosa/models-to-improve-customer-retention
https://www.kaggle.com/prathibhaks/notebookaaa340ee38/edit
data=subset(data, select = -c(`Effective To Date`))
install.packages("MASS")
library(MASS)
full.model <- lm(`Customer Lifetime Value` ~., data = data)
# Stepwise regression model
step.model <- stepAIC(full.model, direction = "both",
trace = FALSE)
summary(step.model)
LS0tDQp0aXRsZTogIkNVU1RPTUVSIExJRkVUSU1FIFZBTFVFIg0Kb3V0cHV0Og0KICBodG1sX2RvY3VtZW50OiBkZWZhdWx0DQogIGh0bWxfbm90ZWJvb2s6IGRlZmF1bHQNCi0tLQ0KPGgyPiBQUk9CTEVNIFNUQVRFTUVOVDwvaDI+DQoNCg0KKkN1c3RvbWVyIExpZmV0aW1lIFZhbHVlIChDTFYpKiBpcyBhIG1lYXN1cmUgb2YgYSBjdXN0b21lcidzIHRvdGFsIHdvcnRoIHRvIGEgYnVzaW5lc3Mgb3ZlciB0aGUgZW50aXJlIHBlcmlvZCBvZiB0aGUgY3VzdG9tZXItYnVzaW5lc3MgcmVsYXRpb25zaGlwLiBUaGUgbWFpbiBvYmplY3RpdmUgb2YgdGhpcyBwcm9qZWN0IGlzIHRvIHByZWRpY3QgdGhlIGxpZmV0aW1lIHZhbHVhdGlvbiBvZiBhIGN1c3RvbWVyIHRvIGZhY2lsaXRhdGUgdGFyZ2V0IG1hcmtldGluZy4gTm90IGFsbCBjdXN0b21lcnMgYXJlIGVxdWFsLiBJbmRlZWQsIHNvbWVvbmUgd2hvIHB1cmNoYXNlcyBhbiBpbmV4cGVuc2l2ZSBwb2xpY3kgaXMgZ29pbmcgdG8gYmUgbGVzcyB2YWx1YWJsZSB0byB5b3VyIGJ1c2luZXNzIHRoYW4gc29tZW9uZSB3aG8gcHVyY2hhc2VzIGFuIGV4cGVuc2l2ZSBvbmUsIGFuZCB5b3VyIGxvbmd0aW1lIGN1c3RvbWVycyB3aWxsIGJyaW5nIGluIG1vcmUgbW9uZXkgdGhhbiB0aG9zZSB3aG8gYnV5IGEgb25lLXllYXIgcG9saWN5IGFuZCBkbyBub3QgcmVuZXcuIEhlcmUgd2UgbmVlZCB0byBwcmVkaWN0IHRoZSBjdXN0b21lciBsaWZldGltZSB2YWx1ZSBmb3IgZWFjaCBjdXN0b21lciB0byBtYWtlIHN1cmUgaG93IG11Y2ggYmVuZWZpdCBlYWNoIGN1c3RvbWVyIGNhbiByZXBheSB0byB0aGUgY29tcGFueSBpbiBleGNoYW5nZSBmb3IgdGhlIGJlbmVmaXRzIGhlL3NoZSByZWNlaXZlcy4gQ0xWIGlzIGFuIGltcG9ydGFudCBmaWd1cmUgdG8ga25vdyBhcyBpdCBoZWxwcyBhIGNvbXBhbnkgdG8gbWFrZSBkZWNpc2lvbnMgYWJvdXQgaG93IG11Y2ggbW9uZXkgdG8gaW52ZXN0IGluIGFjcXVpcmluZyBuZXcgY3VzdG9tZXJzIGFuZCByZXRhaW5pbmcgZXhpc3Rpbmcgb25lcy4NCg0KV2l0aCB0aGUgZ2l2ZW4gaW5mb3JtYXRpb24gcmVnYXJkaW5nIHRoZSBjdXN0b21lcnMsIHdlIGNhbiBwcmVkaWN0IHdoaWNoIGJpZGRpbmcgc3RyYXRlZ2llcyB3aWxsIHlpZWxkIHRoZSBoaWdoZXN0IGxpZmV0aW1lIHJldmVudWVzIGZvciB0aGUgbGVhc3QgYW1vdW50IG9mIG1vbmV5IHRocm91Z2ggZGF0YSBhbmFseXNpcyBhbmQgZXhwbG9yYXRpb24gYW5kIHByZWRpY3QgdGhlIENMViBvZiBhIGdpdmVuIGN1c3RvbWVyLg0KDQpVc2luZyBXYXRzb24gQW5hbHl0aWNzIGRhdGEsIHdlIGNhbiBwcmVkaWN0IGN1c3RvbWVyIGJlaGF2aW9yIHRvIHJldGFpbiBjdXN0b21lcnMuIFdlIGNhbiBhbmFseXplIGFsbCByZWxldmFudCBjdXN0b21lciBkYXRhIGFuZCBkZXZlbG9wIGZvY3VzZWQgY3VzdG9tZXIgcmV0ZW50aW9uIHByb2dyYW1zLlRoZSBxdWVzdGlvbiB0aGF0IHdlIGFyZSB0cnlpbmcgdG8gc29sdmUgaXMgdG8gZGlzY292ZXIgd2hhdCBhZmZlY3RzIGN1c3RvbWVyIGVuZ2FnZW1lbnQgYW5kIHRvIHByb3ZpZGUgYWN0aW9uYWJsZSByZWNvbW1lbmRhdGlvbnMgZm9yIHRoZSBidXNpbmVzcypCdXNpbmVzcyBzdHJhdGVneSBzaG91bGQgYmUgdG8gKiBBY3F1aXJlIG1vcmUgY3VzdG9tZXJzICsgdG8gcmV0YWluIG1vcmUgY3VzdG9tZXJzID0gVG8gaW5jcmVhc2UgY3VzdG9tZXIgcHJvZml0YWJpbGl0eS4NCg0KSW4gdGhpcyBwcm9qZWN0LCBXZSBoYXZlIHRyaWVkIHRvIGZpbmQgdGhlIGVmZmVjdCBvZiBkaWZmZXJlbnQgdmFyaWFibGVzIG9uIHRoZSB0YXJnZXQgdmFyaWFibGUgdGhyb3VnaCB2aXN1YWxpemF0aW9ucywgc3RhdGlzdGljYWwgdGVzdHMgYW5kIHN0YXRpc3RpY2FsIG1vZGVscyBhbmQgY29tcGFyZWQgdGhlIHJlc3VsdHMuDQoNCiMjIyBMb2FkaW5nIExpYnJhcmllcw0KDQpSZXF1aXJlZCBwYWNrYWdlcyBsaWtlIGdncGxvdDIsZHBseXIscGxvdGx5LGx1YnJpZGF0ZSxtb2RlbHIsTWV0cmljcyBhbmQgZmV3IG1vcmUgd2VyZSBsb2FkZWQuDQoNCmBgYHtyIGluY2x1ZGU9RkFMU0V9DQojaW5zdGFsbC5wYWNrYWdlcygiZHBseXIiKQ0KbGlicmFyeShkcGx5cikNCiNpbnN0YWxsLnBhY2thZ2VzKCJ0aWR5dmVyc2UiKQ0KbGlicmFyeSh0aWR5dmVyc2UpDQojaW5zdGFsbC5wYWNrYWdlcygiZ2dwbG90MiIpDQojaW5zdGFsbC5wYWNrYWdlcygnY2FyZXQnLCBkZXBlbmRlbmNpZXMgPSBUUlVFKQ0KbGlicmFyeSgnY2FyZXQnKQ0KbGlicmFyeShnZ3Bsb3QyKQ0KI2luc3RhbGwucGFja2FnZXMoImx1YnJpZGF0ZSIpI1RvIHN0YW5kYXJkaXNlIHRoZSBkYXRlIGNvbHVtbiBjb25zaXN0aW5nIG9mIGRpZmZlcmVudCBmb3JtYXRzDQpsaWJyYXJ5KGx1YnJpZGF0ZSkNCiNpbnN0YWxsLnBhY2thZ2VzKCJwbG90bHkiKQ0KbGlicmFyeShwbG90bHkpDQpsaWJyYXJ5KGdyaWRFeHRyYSkNCiNpbnN0YWxsLnBhY2thZ2VzKCJsYXR0aWNlIikNCmxpYnJhcnkobGF0dGljZSkNCiNpbnN0YWxsLnBhY2thZ2VzKCJyZXByIikNCmxpYnJhcnkocmVwcikNCiNpbnN0YWxsLnBhY2thZ2VzKCJjb3JycGxvdCIpDQpsaWJyYXJ5KGNvcnJwbG90KQ0KI2luc3RhbGwucGFja2FnZXMoIkhNTSIpDQpsaWJyYXJ5KEhNTSkNCiNpbnN0YWxsLnBhY2thZ2VzKCJEVCIpDQpsaWJyYXJ5KERUKQ0KI2luc3RhbGwucGFja2FnZXMoKCJzdHJpbmdpIikpDQpsaWJyYXJ5KHN0cmluZ2kpDQojaW5zdGFsbC5wYWNrYWdlcygibmFuaWFyIikNCmxpYnJhcnkobmFuaWFyKQ0KI2luc3RhbGwucGFja2FnZXMoInNraW1yIikNCmxpYnJhcnkoc2tpbXIpDQojaW5zdGFsbC5wYWNrYWdlcygicmVzaGFwZTIiKQ0KbGlicmFyeShyZXNoYXBlMikNCiNpbnN0YWxsLnBhY2thZ2VzKCJmdW5Nb2RlbGluZyIpDQpsaWJyYXJ5KGZ1bk1vZGVsaW5nKQ0KDQpgYGANCiMjIyBMb2FkaW5nIGRhdGENCg0KRGF0YSB3YXMgbG9hZGVkIGluIGNzdiBmb3JtYXQgYW5kIHdlIHRyaWVkIHRvIGdldCBhIGJhc2ljIGlkZWEgb2YgZGF0YS4gDQpgYGB7ciBpbmNsdWRlPSBGQUxTRX0NCmRhdGEgPC0gcmVhZF9jc3YoIk1hcmtldGluZy1DdXN0b21lci1WYWx1ZS1BbmFseXNpcy5jc3YiKQ0KaGVhZChkYXRhKQ0Kc3RyKGRhdGEpDQoNCmBgYA0KVGhpcyBkYXRhIGhhcyA5MTM0IE9ic2VydmF0aW9ucyBvZiAyNCBkaWZmZXJlbnQgdmFyaWFibGVzLg0KDQpIZXJlLCB0aGUgZGVwZW5kZW50IFZhcmlhYmxlIGlzIEN1c3RvbWVyIExpZmV0aW1lIFZhbHVlLg0KDQpDb250aW51b3VzIEluZGVwZW5kZW50IFZhcmlhYmxlcyBpbmNsdWRlIEN1c3RvbWVyTGlmZXRpbWVWYWx1ZSwgSW5jb21lLE1vbnRobHlQcmVtaXVtQXV0bywgTW9udGhzU2luY2VMYXN0Q2xhaW0sIE1vbnRoc1NpbmNlUG9saWN5SW5jZXB0aW9uLCBOdW1iZXJvZk9wZW5Db21wbGFpbnRzLCBOdW1iZXJvZlBvbGljaWVzIGFuZCBUb3RhbENsYWltQW1vdW50DQoNCkRpc2NyZXRlIEluZGVwZW5kZW50IFZhcmlhYmxlcyBpbmNsdWRlcyBOdW1iZXJvZk9wZW5Db21wbGFpbnRzLCBOdW1iZXJvZlBvbGljaWVzLg0KDQpDYXRlZ29yaWNhbCBJbmRlcGVuZGVudCBWYXJpYWJsZXMgaW5jbHVkZSBTdGF0ZSwgUmVzcG9uc2UsIENvdmVyYWdlLCBFZHVjYXRpb24sIEVtcGxveW1lbnRTdGF0dXMsIEdlbmRlciwgTG9jYXRpb25Db2RlLCBNYXJpdGFsU3RhdHVzLCBOdW1iZXJvZk9wZW5Db21wbGFpbnRzLCBOdW1iZXJvZlBvbGljaWVzLFBvbGljeVR5cGUsIFBvbGljeSwgUmVuZXdPZmZlclR5cGUsIFNhbGVzQ2hhbm5lbCwgVmVoaWNsZUNsYXNzIGFuZCBWZWhpY2xlU2l6ZQ0KDQpPdGhlciBmdW5jdGlvbnMgbGlrZSBkZXNjcmliZSxkaW0sc2FwcGx5IHdlcmUgYWxzbyB1c2VkIGZvciBiZXR0ZXIgdW5kZXJzdGFuZGluZyBvZiBkYXRhLg0KDQojIyBQcmUtcHJvY2Vzc2luZyBkYXRhDQoNCiMjIyBNaXNzaW5nIHZhbHVlIGFuYWx5c2lzIA0KYGBge3Igd2FybmluZz1GQUxTRSxmaWcuYWxpZ249ImNlbnRlciIsIGVjaG8gPSBGQUxTRSxmaWcud2lkdGggPSAxNH0NCmdnX21pc3NfdmFyKGRhdGEpDQpgYGANCg0KDQpXZSBoYXZlIG5vIG1pc3Npbmcgb3IgZHVwbGljYXRlIHZhbHVlcyBpbiBkYXRhIHNvIHdlIG5lZWQgbm90IGRvIG1pc3NpbmcgdmFsdWUgdHJlYXRtZW50Lg0KDQojIyMgT3V0bGllciBBbmFseXNpcw0KYGBge3J9DQpwcm9maWxpbmdfbnVtKGRhdGEpDQpgYGANClNrZXduZXNzIGVzc2VudGlhbGx5IG1lYXN1cmVzIHRoZSBzeW1tZXRyeSBvZiB0aGUgZGlzdHJpYnV0aW9uLCB3aGlsZSBrdXJ0b3NpcyBkZXRlcm1pbmVzIHRoZSBoZWF2aW5lc3Mgb2YgdGhlIGRpc3RyaWJ1dGlvbiB0YWlscy5UaGVzZSB0d28gc3RhdGlzdGljcyBnYXZlIHVzIGluc2lnaHRzIGludG8gdGhlIHNoYXBlIG9mIGRpc3RyaWJ1dGlvbi4gSGlnaCBrdXJ0b3NpcyBpbiBhIGRhdGEgc2V0IGlzIGFuIGluZGljYXRvciB0aGF0IGRhdGEgaGFzIGhlYXZ5IG91dGxpZXJzLiBMb3cga3VydG9zaXMgaW4gYSBkYXRhIHNldCBpcyBhbiBpbmRpY2F0b3IgdGhhdCBkYXRhIGhhcyBsYWNrIG9mIG91dGxpZXJzLiBTbywgaGlnaCB2YWx1ZSBvZiBrdXJ0b3NpcyBkaXNwbGF5cyBwcmVzZW5jZSBvZiBvdXRsaWVycyBmb3IgQ3VzdG9tZXIgTGlmZXRpbWUgVmFsdWUsVG90YWwgQ2xhaW0gQW1vdW50LE51bWJlciBvZiBPcGVuIENvbXBsYWludHMsIE1vbnRobHkgUHJlbWl1bSBBdXRvIGNvbHVtbnMsIHdoZXJlIGt1cnRvc2lzIHZhbHVlIGlzIG1vcmUgdGhhbiAzLg0KDQpQZXJjZW50aWxlcyBhcmUgb2JzZXJ2ZWQgdG8gc2VlIGNoYW5nZSBpbiBjb250aW51b3VzIHZhcmlhYmxlcy4gSGVyZSwgd2Ugb2JzZXJ2ZSB0aGF0IHRoZXJlIGlzIG5vIHN1ZGRlbiBqdW1wIGluIHRoZSB2YWx1ZXMsIHdoaWNoIG1lYW5zIHRoZXJlIGFyZSAgYW5vbWFseS4gQW5kIG91dGxpZXJzIG9mIENMViBhY3R1YWxseSByZXByZXNlbnQgaW1wb3J0YW50IGN1c3RvbWVycyxhbmQgZG9pbmcgb3V0bGllciB0cmVhdG1lbnQgZGlyZWN0bHkgd291bGQgbWVhbiB3ZSBhcmUgbG9zaW5nIGdvb2QgY2xpZW50cyBmb3IgdGhlIGNvbXBhbnkuDQoNCiMjIyBEYXRhIFRyYW5zZm9ybWF0aW9uDQpgYGB7cn0NCmRhdGEkYEVmZmVjdGl2ZSBUbyBEYXRlYDwtbWR5KGRhdGEkYEVmZmVjdGl2ZSBUbyBEYXRlYCkNCg0KIyBDb252ZXJ0aW5nIGNoYXJhY3RlciB2YXJpYWJsZXMgaW50byBGYWN0b3IgdmFyaWFibGVzDQoNCmRhdGEkU3RhdGUgPC0gYXMuZmFjdG9yKGRhdGEkU3RhdGUpDQpkYXRhJFJlc3BvbnNlIDwtIGFzLmZhY3RvcihkYXRhJFJlc3BvbnNlKQ0KZGF0YSRDb3ZlcmFnZSA8LSBhcy5mYWN0b3IoZGF0YSRDb3ZlcmFnZSkNCmRhdGEkRWR1Y2F0aW9uIDwtIGFzLmZhY3RvcihkYXRhJEVkdWNhdGlvbikNCmRhdGEkRW1wbG95bWVudFN0YXR1cyA8LSBhcy5mYWN0b3IoZGF0YSRFbXBsb3ltZW50U3RhdHVzKQ0KZGF0YSRHZW5kZXIgPC0gYXMuZmFjdG9yKGRhdGEkR2VuZGVyKQ0KZGF0YSRgTG9jYXRpb24gQ29kZWAgIDwtIGFzLmZhY3RvcihkYXRhJGBMb2NhdGlvbiBDb2RlYCApDQpkYXRhJGBNYXJpdGFsIFN0YXR1c2AgIDwtIGFzLmZhY3RvcihkYXRhJGBNYXJpdGFsIFN0YXR1c2ApDQpkYXRhJGBQb2xpY3kgVHlwZWAgPC0gYXMuZmFjdG9yKGRhdGEkYFBvbGljeSBUeXBlYCkNCmRhdGEkYFJlbmV3IE9mZmVyIFR5cGVgPC0gYXMuZmFjdG9yKGRhdGEkYFJlbmV3IE9mZmVyIFR5cGVgKQ0KZGF0YSRQb2xpY3kgIDwtIGFzLmZhY3RvcihkYXRhJFBvbGljeSkNCmRhdGEkYFNhbGVzIENoYW5uZWxgICA8LSBhcy5mYWN0b3IoZGF0YSRgU2FsZXMgQ2hhbm5lbGApDQpkYXRhJGBWZWhpY2xlIENsYXNzYCAgPC0gYXMuZmFjdG9yKGRhdGEkYFZlaGljbGUgQ2xhc3NgKQ0KZGF0YSRgVmVoaWNsZSBTaXplYCAgPC0gYXMuZmFjdG9yKGRhdGEkYFZlaGljbGUgU2l6ZWApDQoNCg0KIyBDb252ZXJ0aW5nIHR3byBudW1lcmljYWwgdmFyaWFibGVzIGFzIGZhY3RvcnMNCmRhdGEkYE51bWJlciBvZiBPcGVuIENvbXBsYWludHNgIDwtIGFzLmZhY3RvcihkYXRhJGBOdW1iZXIgb2YgT3BlbiBDb21wbGFpbnRzYCkNCmRhdGEkYE51bWJlciBvZiBQb2xpY2llc2A8LSBhcy5mYWN0b3IoZGF0YSRgTnVtYmVyIG9mIFBvbGljaWVzYCkNCg0KYGBgDQpXZSBjb252ZXJ0ZWQgYWxsIHRoZSBjYXRlZ29yaWNhbCB2YXJpYWJsZXMgYW5kIDIgZGlzY3JldGUgbnVtZXJpYyBjb2x1bW5zKE51bWJlciBvZiBwb2xpY2llcyBhbmQgbnVtYmVyIG9mIG9wZW4gY29tcGxhaW50cykNCmludG8gZmFjdG9ycyB0byBnaXZlIGVhY2ggY2F0ZWdvcnkgYSBsZXZlbC4gU2luY2UgdGhlIGZvcm1hdCBvZiBkYXRlIGNvbHVtbiB3YXMgbm90IHVuaWZvcm0gd2Ugc3RhbmRhcmRpemVkIGl0Lg0KDQojIyBFeHBsb3JhdG9yeSBEYXRhIEFuYWx5c2lzDQoNCg0KVG8gaWRlbnRpZnkgcXVhbnRpdHkgYW5kIHBlcmNlbnRhZ2Ugb2YgemVyb3MNCg0KYGBge3J9DQpzdGF0dXMoZGF0YSkNCmBgYA0KDQpGcm9tIGhlcmUsIHdlIGNhbiBvYnNlcnZlIHRoYXQgdGhlcmUgYXJlIGxvdCBvZiBwZW9wbGUgd2hvc2UgaW5jb21lIGlzIDAgc2luY2UgdGhleSBhcmUgdW5lbXBsb3llZC4NCg0KT3VyIG9iamVjdGl2ZSBoZXJlIGlzIHRvIHZpc3VhbGl6ZSB0aGUgZ2l2ZW4gZGF0YSBhbmQgbG9vayBmb3IgdmFyaWFibGVzIHRoYXQgY2FuIGJlIGltcG9ydGFudCBmb3IgbW9kZWxsaW5nLg0KDQojIyMgQ3VzdG9tZXIgTGlmZXRpbWUgVmFsdWUgDQpUaGlzIGlzIG91ciB0YXJnZXQgdmFyaWFibGUuDQoNCmBgYHtyIHdhcm5pbmc9RkFMU0V9DQoNCmZpZ19DTFYgPC0gcGxvdF9seSh4ID1kYXRhJGBDdXN0b21lciBMaWZldGltZSBWYWx1ZWAsIHR5cGUgPSAiaGlzdG9ncmFtIiklPiUgbGF5b3V0KHRpdGxlID0iIENMViBESVNUUklCVVRJT04gSU4gVEhFIERBVEEiKQ0KZmlnX0NMVg0KDQoNCmBgYA0KQ3VzdG9tZXIgbGlmZXRpbWUgdmFsdWUgaXMgcG9zaXRpdmVseSBza2V3ZWQuDQoNCiMjIyBSZXNwb25zZQ0KDQpgYGB7cn0NCg0KZ2dwbG90KGRhdGEsYWVzKFJlc3BvbnNlKSkrZ2VvbV9iYXIoZmlsbD0icGluayIsY29sPSJibGFjayIsd2lkdGg9MC43LHBvc2l0aW9uPXBvc2l0aW9uX2RvZGdlKDAuOSkpKw0KICAgIGdlb21fdGV4dChzdGF0PSJjb3VudCIsYWVzKGxhYmVsID0gYWZ0ZXJfc3RhdChjb3VudCkpLHZqdXN0PTIpKw0KICAgIHRoZW1lKA0KICAgICAgICB0ZXh0PWVsZW1lbnRfdGV4dChzaXplPTEwKSwNCiAgICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KGNvbG9yPSJibGFjayIsIHNpemU9MTUpLA0KICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoY29sb3I9ImJsYWNrIiwgc2l6ZT0xNSkNCiAgICApDQoNCg0KYGBgDQoNCmBgYHtyfQ0KZ2dwbG90KGRhdGEsIGFlcyh4ID1SZXNwb25zZSwgeSA9IGBDdXN0b21lciBMaWZldGltZSBWYWx1ZWApKSArDQogIGdlb21fYm94cGxvdCgpICsgDQogIHhsYWIoIlJlc3BvbnNlIG9mIGN1c3RvbWVycyIpICsgeWxhYigiQ0xWIikNCmBgYA0KDQoNCkhlcmUsIHdlIGNhbiBvYnNlcnZlIHRoYXQgdmVyeSBmZXcgcGVvcGxlIGhhdmUgcmVhcHBsaWVkIGZvciBwb2xpY3kuIFNvLCBpZiBvdXIgZm9jdXMgaXMgdG8gcmV0YWluIGN1c3RvbWVycyB3ZSBzaG91bGQgdGFyZ2V0IGN1c3RvbWVycyB3aG8gc2FpZCBOTyBhcyB0aG9zZSBjdXN0b21lcnMgd2hvIGhhdmUgc2FpZCBubyBhcmUgb2YgaGlnaCBjdXN0b21lciBsaWZldGltZSB2YWx1ZS4NCg0KIyMjIENvdmVyYWdlDQoNCg0KYGBge3J9DQpnZ3Bsb3QoZGF0YSwgYWVzKHggPUNvdmVyYWdlLCB5ID0gYEN1c3RvbWVyIExpZmV0aW1lIFZhbHVlYCkpICsNCiAgZ2VvbV9ib3hwbG90KCkgKyANCiAgeGxhYigiQ292ZXJhZ2Ugb2YgY3VzdG9tZXJzIikgKyB5bGFiKCJDTFYiKQ0KYGBgDQoNCmBgYHtyfQ0KYWdncmVnYXRlKCBgQ3VzdG9tZXIgTGlmZXRpbWUgVmFsdWVgIH4gQ292ZXJhZ2UsIGRhdGEsIG1lYW4pDQpgYGANClRoaXMgY2FuIGJlIGFuIGltcG9ydGFudCB2YXJpYWJsZSwgYXMgZGlmZmVyZW50IGdyb3VwcyBzaG93IG1ham9yIGRpZmZlcmVuY2VzIGluIHRoZWlyIHZhbHVlLCBhbmQgd2hpY2gga2luZCBvZiBjb3ZlcmFnZSB0aGV5IGFyZSBjaG9vc2luZyBtYXkgaGVscCBpbiBkZWNpZGluZyB0aGVpciBjdXN0b21lciBsaWZlIHRpbWUgdmFsdWUuDQoNCg0KIyMjIEVkdWNhdGlvbg0KDQoNCg0KYGBge3J9DQpnZ3Bsb3QoZGF0YSwgYWVzKHggPUVkdWNhdGlvbiwgeSA9IGBDdXN0b21lciBMaWZldGltZSBWYWx1ZWApKSArDQogIGdlb21fYm94cGxvdCgpICsgDQogIHhsYWIoIkVkdWNhdGlvbiBvZiBjdXN0b21lcnMiKSArIHlsYWIoIkNMViIpDQpgYGANCg0KDQpEb2N0b3IgaW4gZ2VuZXJhbCBoYXZlIGxvdyBjdXN0b21lciBsaWZlIHRpbWUgdmFsdWUsIHdoaWxlIE1hc3RlcnMgYW5kIEhpZ2ggU2Nob29sIG9yIEJlbG93IGhhdmUgaGlnaGVyIGN1c3RvbWVyIGxpZmUgdGltZSB2YWx1ZS4gQnV0IGl0J3Mgc3RpbGwgdG91Z2ggdG8gb2JzZXJ2ZSBhbnkgbWFqb3IgY2hhbmdlcyBhY3Jvc3MgZGlmZmVyZW50IGdyb3Vwcy4gU28sIGl0IG1heSBiZSBwb3NzaWJsZSB0aGF0IHRoaXMgdmFyaWFibGUgaXMgbm90IGltcG9ydGFudC4NCg0KIyMjIEVtcGxveW1lbnQgU3RhdHVzDQoNCg0KYGBge3J9DQpzdHJpcHBsb3QofmBDdXN0b21lciBMaWZldGltZSBWYWx1ZWB8RW1wbG95bWVudFN0YXR1cyxkYXRhLA0KICBwYW5lbD1mdW5jdGlvbih4LHksLi4uKSB7DQogICAgIG09bWVkaWFuKHgpDQogICAgIHBhbmVsLnN0cmlwcGxvdCh4LHksLi4uKQ0KICAgICBwYW5lbC5zdHJpcHBsb3QobSx5LHBjaD0ifCIsY2V4PTIsY29sPTIpDQogIH0NCikNCg0KYGBgDQoNCmBgYHtyfQ0KZ2dwbG90KGRhdGEsIGFlcyh4ID1FbXBsb3ltZW50U3RhdHVzLCB5ID0gYEN1c3RvbWVyIExpZmV0aW1lIFZhbHVlYCkpICsNCiAgZ2VvbV9ib3hwbG90KCkgKyANCiAgeGxhYigiRW1wbG95bWVudFN0YXR1cyBvZiBjdXN0b21lcnMiKSArIHlsYWIoIkNMViIpDQoNCmBgYA0KDQoNCkN1c3RvbWVyIHdpdGggaGlnaCBjdXN0b21lciBsaWZldGltZSB2YWx1ZXMgbGllcyBtYWlubHkgaW4gRW1wbG95ZWQgYW5kIHVuZW1wbG95ZWQgY2F0ZWdvcmllcywgd2hpbGUgaXQgYXBwZWFycyB0aGF0IHRoZXJlIGlzbid0IGFueSBtYWpvciBkaWZmZXJlbmNlIGFjcm9zcyBjYXRlZ29yaWVzLiBBbHNvLCB3ZSBjYW4gc2F5IHRoYXQgcGVvcGxlIHdobyBhcmUgUmV0aXJlZCwgb24gTWVkaWNhbCBsZWF2ZSwgRGlzYWJsZWQgdGhlaXIgY3VzdG9tZXIgbGlmZXRpbWUgdmFsdWUgaXMgbGVzcy4gRW1wbG95ZWQgcGVvcGxlJ3MgY3VzdG9tZXIgbGlmZXRpbWUgdmFsdWUgZGVmaW5pdGVseSB0dXJucyBvdXQgdG8gYmUgaGlnaGVzdCBhbW9uZyBhbGwuIFRoZXJlIGlzIG9uZSBkYXRhIHBvaW50IGluIGRpc2FibGVkIHdoaWNoIGNhbiBiZSB0cmVhdGVkIGFzIG91dGxpZXIgYXMgaXQncyB2YWx1ZSBpcyByZWFsbHkgaGlnaCwgd2hpY2ggZG9lc24ndCBtYWtlIGl0IGEgZ2VuZXJhbCBjYXNlLiBTbywgd2UgcmVtb3ZlIHRoYXQgZGF0YSBwb2ludC4NCg0KDQoNCmBgYHtyfQ0Kc3Vic2V0KGRhdGEsRW1wbG95bWVudFN0YXR1cz09J0Rpc2FibGVkJyYgIkN1c3RvbWVyIExpZmV0aW1lIFZhbHVlIj40MDAwMCkNCg0KYGBgDQpgYGB7cn0NCmRhdGE9ZGF0YVtkYXRhJEN1c3RvbWVyICE9J1hGODk5MDYnLF0NCg0KYGBgDQpUaGlzIHJvdyBpcyBkcm9wcGVkLg0KDQojIyMgR2VuZGVyDQpgYGB7cn0NCmdncGxvdChkYXRhLGFlcyhHZW5kZXIpKStnZW9tX2JhcihmaWxsPSJncmV5Iixjb2w9ImJsYWNrIix3aWR0aD0wLjcscG9zaXRpb249cG9zaXRpb25fZG9kZ2UoMC45KSkrDQogICAgZ2VvbV90ZXh0KHN0YXQ9ImNvdW50IixhZXMobGFiZWwgPSBhZnRlcl9zdGF0KGNvdW50KSksdmp1c3Q9MikrDQogICAgdGhlbWUoDQogICAgICAgIHRleHQ9ZWxlbWVudF90ZXh0KHNpemU9MTApLA0KICAgICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoY29sb3I9ImJsYWNrIiwgc2l6ZT0xNSksDQogICAgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChjb2xvcj0iYmxhY2siLCBzaXplPTE1KQ0KICAgICkNCmBgYA0KDQoNCg0KYGBge3J9DQpnZ3Bsb3QoZGF0YSwgYWVzKHggPUdlbmRlciwgeSA9IGBDdXN0b21lciBMaWZldGltZSBWYWx1ZWApKSArDQogIGdlb21fYm94cGxvdCgpICsgDQogIHhsYWIoIkdlbmRlciIpICsgeWxhYigiQ0xWIikNCmBgYA0KDQpJdCBhcHBlYXJzIHRoYXQgZ2VuZGVyIGhhcyBubyBlZmZlY3Qgb24gY3VzdG9tZXIgbGlmZXRpbWUgdmFsdWUuDQoNCiMjIyBMb2NhdGlvbiBDb2RlDQpgYGB7cn0NCmdncGxvdChkYXRhLGFlcyhgTG9jYXRpb24gQ29kZWApKStnZW9tX2JhcihmaWxsPSJncmV5Iixjb2w9ImJsYWNrIix3aWR0aD0wLjcscG9zaXRpb249cG9zaXRpb25fZG9kZ2UoMC45KSkrDQogICAgZ2VvbV90ZXh0KHN0YXQ9ImNvdW50IixhZXMobGFiZWwgPSBhZnRlcl9zdGF0KGNvdW50KSksdmp1c3Q9MikrDQogICAgdGhlbWUoDQogICAgICAgIHRleHQ9ZWxlbWVudF90ZXh0KHNpemU9MTApLA0KICAgICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoY29sb3I9ImJsYWNrIiwgc2l6ZT0xNSksDQogICAgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChjb2xvcj0iYmxhY2siLCBzaXplPTE1KQ0KICAgICkNCmBgYA0KDQoNCmBgYHtyfQ0KYWdncmVnYXRlKCBgQ3VzdG9tZXIgTGlmZXRpbWUgVmFsdWVgIH4gYExvY2F0aW9uIENvZGVgLCBkYXRhLCBtZWFuKQ0KYGBgDQpXZSBzaG91bGQgZm9jdXMgbW9yZSBvbiBzdWJ1cmJhbnMuIGFzIHRoZXkgYXJlIG91ciBtYWpvciBjdXN0b21lcnMgV2UgY2FuJ3Qgc2F5IGFueSBtYWpvciBkaWZmZXJlbmNlIGJldHdlZW4gdGhlc2UgZ3JvdXBzLldlIGdldCBzYW1lIGluZmVyZW5jZSB1c2luZyBib3hwbG90cyBhcyB3ZWxsLg0KDQoNCiMjIyBWZWhpY2xlIENsYXNzDQoNCmBgYHtyfQ0KZ2dwbG90KGRhdGEsIGFlcyh4ID1gVmVoaWNsZSBDbGFzc2AsIHkgPSBgQ3VzdG9tZXIgTGlmZXRpbWUgVmFsdWVgKSkgKw0KICBnZW9tX2JveHBsb3QoKSArIA0KICB4bGFiKCJWZWhpY2xlIENsYXNzIG9mIGN1c3RvbWVycyIpICsgeWxhYigiQ0xWIikNCmBgYA0KDQpgYGB7cn0NCmFnZ3JlZ2F0ZSggYEN1c3RvbWVyIExpZmV0aW1lIFZhbHVlYCB+IGBWZWhpY2xlIENsYXNzYCwgZGF0YSwgbWVhbikNCmhlYWQoZGF0YSkNCmBgYA0KTHV4dXJ5IENhcixMdXh1cnkgU1VWIGhhdmUgaGlnaCBjdXN0b21lciBsaWZldGltZSB2YWx1ZSwgd2hpbGUgRm91ci1Eb29yIENhciBhbmQgVHdvLURvb3IgQ2FyIGhhdmUgbGVzcyBjdXN0b21lciBsaWZldGltZSB2YWx1ZXMuIFNvLCB0aGlzIHZhcmlhYmxlIG1heSBiZSBpbXBvcnRhbnQgZm9yIHByZWRpY3Rpb24gYXMgaXQgdmFyaWVzIGFjcm9zcyBkaWZmZXJlbnQgY2F0ZWdvcmllcy4NCg0KIyMjIFNhbGVzIENoYW5uZWwNCg0KYGBge3J9DQpoZWFkKGRhdGEpDQoNCmdncGxvdChkYXRhLCBhZXMoeCA9YFNhbGVzIENoYW5uZWxgLCB5ID0gYEN1c3RvbWVyIExpZmV0aW1lIFZhbHVlYCkpICsNCiAgZ2VvbV9ib3hwbG90KCkgKyANCiAgeGxhYigiU2FsZXMgQ2hhbm5lbCBvZiBjdXN0b21lcnMiKSANCmBgYA0KDQoNCk5vdCBtdWNoIGRpZmZlcmVuY2UgaXMgb2JzZXJ2ZWQgYWNyb3NzIGRpZmZlcmVudCBjYXRlZ29yaWVzLCBidXQgd2UgY2FuIHNlZSBwcmVzZW5jZSBvZiBhbiBvdXRsaWVyLiBTbywgd2Ugd2lsbCB0cmVhdCBpdC4NCg0KYGBge3J9DQpoZWFkKGRhdGEpDQpzdWJzZXQoZGF0YSxgU2FsZXMgQ2hhbm5lbGA9PSdDYWxsIENlbnRlcicgJiAnQ3VzdG9tZXIgTGlmZXRpbWUgVmFsdWUnPjUwMDAwKQ0KYGBgDQoNCg0KYGBge3J9DQpkYXRhPWRhdGFbZGF0YSRDdXN0b21lciAhPSdGUTYxMjgxJyxdDQpgYGANCiMjIyBSZW5ldyBPZmZlciBUeXBlDQoNCg0KYGBge3J9DQpnZ3Bsb3QoZGF0YSwgYWVzKHggPWBSZW5ldyBPZmZlciBUeXBlYCwgeSA9YEN1c3RvbWVyIExpZmV0aW1lIFZhbHVlYCkpICsNCiAgZ2VvbV9ib3hwbG90KCkgKyANCiAgeGxhYigiUmVuZXcgT2ZmZXIgVHlwZSBvZiBjdXN0b21lcnMiKQ0KYGBgDQoNCg0KYGBge3J9DQoNCmFnZ3JlZ2F0ZSggYEN1c3RvbWVyIExpZmV0aW1lIFZhbHVlYCB+IGBSZW5ldyBPZmZlciBUeXBlYCwgZGF0YSwgbWVhbikNCmBgYA0KDQoNCk9mZmVyIDEgYW5kIE9mZmVyIDMgc2VlbXMgdG8gcmVwcmVzZW50IGxpdHRsZSBoaWdoZXIgdmFsdWVkIGN1c3RvbWVycy4gQnV0IHdlIGNhbid0IHNheSBpZiBkaWZmZXJlbmNlIGFjcm9zcyBjYXRlZ29yaWVzIGlzIHNpZ25pZmljYW50IGVub3VnaC4NCg0KDQojIyMgUG9saWN5IFR5cGUNCmBgYHtyfQ0KZ2dwbG90KGRhdGEsIGFlcyh4ID1gUG9saWN5IFR5cGVgLHkgPWBDdXN0b21lciBMaWZldGltZSBWYWx1ZWApKSArDQogIGdlb21fYm94cGxvdCgpICsgDQogIHhsYWIoIlBvbGljeSB0eXBlIG9mIGN1c3RvbWVycyIpICsgeWxhYigiQ0xWIikNCmBgYA0KDQpgYGB7cn0NCmFnZ3JlZ2F0ZSggYEN1c3RvbWVyIExpZmV0aW1lIFZhbHVlYCB+IGBQb2xpY3kgVHlwZWAsIGRhdGEsIG1lYW4pDQpgYGANCg0KDQpTcGVjaWFsIEF1dG8gcmFuZ2Ugb2YgY3VzdG9tZXIgbGlmZSB0aW1lIHZhbHVlcyBpcyBpbiBnZW5lcmFsIGhpZ2hlciB0aGFuIG90aGVycywgd2hpbGUgUGVyc29uYWwgQXV0byBwb2xpY3kgdHlwZSBpcyB0aGUgb25lIHRoYXQgaXMgZ2VuZXJhbGx5IG9wdGVkIGJ5IGN1c3RvbWVycy4NCg0KIyMgUG9saWN5DQoNCmBgYHtyIGZpZy53aWR0aD0xMH0NCmdncGxvdChkYXRhLCBhZXMoeCA9UG9saWN5LCB5ID0gYEN1c3RvbWVyIExpZmV0aW1lIFZhbHVlYCkpICsNCiAgZ2VvbV9ib3hwbG90KCkgKyANCiAgeGxhYigiUG9saWN5IG9mIGN1c3RvbWVycyIpICsgeWxhYigiQ0xWIikNCmBgYA0KDQoNCmBgYHtyfQ0KYWdncmVnYXRlKCBgQ3VzdG9tZXIgTGlmZXRpbWUgVmFsdWVgIH4gUG9saWN5LCBkYXRhLCBtZWFuKQ0KYGBgDQpEaWZmZXJlbmNlIGFjcm9zcyBkaWZmZXJlbnQgY2F0ZWdvcmllcyBzZWVtcyB0byBiZSBsZXNzLCBidXQgbWF5IGJlIHNpZ25pZmljYW50IGFjcm9zcyBzb21lIGNhdGVnb3JpZXMuIFRoaXMgdmFyaWFibGUgc2VlbXMgdG8gYmUgaGlnaGx5IGNvcnJlbGF0ZWQgdG8gUmVuZXcgT2ZmZXIgVHlwZSwgc28gd2UgbmVlZCB0byBkcm9wIG9uZSBvZiB0aGVzZSB2YXJpYWJsZXMuDQoNCiMjIENvbnRpbnVvdXMgdmFyaWFibGVzDQpgYGB7cn0NCmdncGxvdChkYXRhLCBhZXMoeCA9IEluY29tZSwgeSA9IGBDdXN0b21lciBMaWZldGltZSBWYWx1ZWApKSArIGdlb21fcG9pbnQoKQ0KDQpgYGANCg0KDQpgYGB7cn0NCmdncGxvdChkYXRhLCBhZXMoeCA9IGBNb250aGx5IFByZW1pdW0gQXV0b2AsIHkgPSBgQ3VzdG9tZXIgTGlmZXRpbWUgVmFsdWVgKSkgKyBnZW9tX3BvaW50KCkNCmBgYA0KDQpgYGB7cn0NCmdncGxvdChkYXRhLCBhZXMoeCA9YFRvdGFsIENsYWltIEFtb3VudGAsIHkgPSBgQ3VzdG9tZXIgTGlmZXRpbWUgVmFsdWVgKSkgKyBnZW9tX3BvaW50KCkNCg0KYGBgDQoNCg0KTW9udGhzIFNpbmNlIFBvbGljeSBJbmNlcHRpb24sIE1vbnRocyBTaW5jZSBMYXN0IENsYWltLEluY29tZSBkb2Vzbid0IHNob3cgbXVjaCBjb3JyZWxhdGlvbiB3aXRoIEN1c3RvbWVyIExpZmV0aW1lIHZhbHVlLiBXaGlsZSBUb3RhbCBDbGFpbSBBbW91bnQsIE1vbnRobHkgUHJlbWl1bSBBdXRvIGFwcGVhcnMgdG8gaGF2ZSBzb21lIGNvcnJlbGF0aW9uIHRvIGRlcGVuZGVudCB2YXJpYWJsZSBDdXN0b21lciBMaWZldGltZSB2YWx1ZS4NCg0KDQojIyBDb3JyZWxhdGlvbiBNYXRyaXgNCmBgYHtyIHdhcm5pbmc9RkFMU0V9DQpsaWJyYXJ5KGdnY29ycnBsb3QpDQpsaWJyYXJ5KCJkcGx5ciIpDQpkYXRhX251bT1zZWxlY3RfaWYoZGF0YSwgaXMubnVtZXJpYykNCmNvcnIgPC0gcm91bmQoY29yKGRhdGFfbnVtKSwgMykNCmdnY29ycnBsb3QoY29ycikNCmBgYA0KDQpGcm9tIGNvcnJlbGF0aW9uIG1hdHJpeCB3ZSBjYW4gc2F5IHRoYXQgSW5jb21lLCBUb3RhbCBjbGFpbSBhbW91bnQgLCBNb250aGx5IFByZW1pdW0gQXV0byBhcmUgY29ycmVsYXRlZCB0byBlYWNoIG90aGVyLiBTbywgd2Ugd2lsbCBkcm9wIDIgdmFyaWFibGVzIG91dCBvZiB0aGVzZSAzIHRvIHByZXZlbnQgbXVsdGljb2xsaW5lYXJpdHkuDQoNCkFsc28sIHdlIGNhbiBzZWUgdGhhdCBkZXBlbmRlbnQgdmFyaWFibGUgQ3VzdG9tZXIgbGlmZXRpbWUgdmFsdWUgaGFzIGhpZ2ggY29ycmVsYXRpb24gdG8gTW9udGhseSBQcmVtaXVtIGF1dG8sIFRvdGFsIGNsYWltIGFtb3VudC4gU28sIHRoZXNlIGNhbiBiZSBjb25zaWRlcmVkIGFzIGltcG9ydGFudCB2YXJpYWJsZXMgYnV0IGR1ZSB0byBwcmVzZW5jZSBvZiBtdWx0aWNvbGxpbmVhcml0eSBhbW9uZyB0aGVzZSB0d28gd2Ugd2lsbCBtb3N0IHByb2JhYmx5IHVzZSBqdXN0IG9uZSB2YXJpYWJsZS4NCg0KIyMgRGVyaXZpbmcgTmV3IENvbHVtbg0KYGBge3J9DQpkYXRhJCJQcmVzZW50IFZhbHVlIE9mIEN1c3RvbWVyIj0gKGRhdGEkIk1vbnRobHkgUHJlbWl1bSBBdXRvIiAqIGRhdGEkIk1vbnRocyBTaW5jZSBQb2xpY3kgSW5jZXB0aW9uIikgLSBkYXRhJCJUb3RhbCBDbGFpbSBBbW91bnQiDQpnZ3Bsb3QoZGF0YSwgYWVzKHggPWBQcmVzZW50IFZhbHVlIE9mIEN1c3RvbWVyYCwgeSA9IGBDdXN0b21lciBMaWZldGltZSBWYWx1ZWApKSArIGdlb21fcG9pbnQoKQ0KYGBgDQpUaGVyZSBpcyBoaWdoIGNvcnJlbGF0aW9uIGJldHdlZW4gQ3VzdG9tZXIgTGlmZXRpbWUgdmFsdWUsIGFuZCB0aGUgZGVyaXZlZCB2YXJpYWJsZSBQcmVzZW50IHZhbHVlIG9mIGN1c3RvbWVyLiBXZSB3aWxsIHRyeSB0byB1c2UgdGhpcyB2YXJpYWJsZSBmb3IgbW9kZWxsaW5nLCBhbmQgY2hlY2sgaWYgaXQgaGVscHMgdG8gaW1wcm92ZSByZXN1bHQuDQoNCiMjIEJpbm5pbmcgQ29sdW1ucw0KYGBge3J9DQojIEFkZCBJbmNvbWUgYmlucyBhcyBuZXcgY29sdW1uDQpkYXRhJCJJbmNvbWUgQmluIiA8LSBjdXQoZGF0YSQiSW5jb21lIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICBicmVha3MgPSBjKC0xLDE0OTk5LDI5OTk5LDQ0OTk5LDU5OTk5LDc0OTk5LEluZiksDQogICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gYygiPCAkMTUwMDAiLCAiJDE1MDAwLTI5OTk5IiwiJDMwMDAwLTQ0OTk5IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICIkNDUwMDAtNTk5OTkiLCAiJDYwMDAwLTc0OTk5IiwgIiQ3NTAwMCsiKSkNCg0KIyBBZGQgTW9udGhseSBQcmVtaXVtIGJpbnMgYXMgbmV3IGNvbHVtbg0KZGF0YSQiTW9udGhseSBQcmVtaXVtIEJpbiIgPC0gY3V0KGRhdGEkIk1vbnRobHkgUHJlbWl1bSBBdXRvIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWtzID0gYygwLDc0LDk5LDEyNCwxNDksSW5mKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKCI8ICQ3NSIsICIkNzUtOTkiLCIkMTAwLTEyNCIsIiQxMjUtMTQ5IiwiJDE1MCsiKSkNCg0KIyBBZGQgVG90YWwgQ2xhaW0gYmlucyBhcyBuZXcgY29sdW1uDQpkYXRhJCJUb3RhbCBDbGFpbSBCaW4iIDwtIGN1dChkYXRhJCJUb3RhbCBDbGFpbSBBbW91bnQiLCANCiAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IGMoMCwyNDksNDk5LDc0OSw5OTksSW5mKSwNCiAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IGMoIjwgJDI1MCIsICIkMjUwLTQ5OSIsIiQ1MDAtNzQ5IiwiJDc1MC05OTkiLCIkMTAwMCsiKSkNCmBgYA0KDQpgYGB7cn0NCmdncGxvdChkYXRhLCBhZXMoeCA9YE1vbnRobHkgUHJlbWl1bSBCaW5gLCB5ID0gYEN1c3RvbWVyIExpZmV0aW1lIFZhbHVlYCkpICsNCiAgZ2VvbV9ib3hwbG90KCkgKyANCiAgeGxhYigiTW9udGhseSBQcmVtaXVtIEJpbiBvZiBjdXN0b21lcnMiKSArIHlsYWIoIkNMViIpDQoNCmBgYA0KDQoNCmBgYHtyfQ0KDQphZ2dyZWdhdGUoIGBDdXN0b21lciBMaWZldGltZSBWYWx1ZWAgfiBgTW9udGhseSBQcmVtaXVtIEJpbmAsIGRhdGEsIG1lYW4pDQpgYGANCg0KYGBge3J9DQpnZ3Bsb3QoZGF0YSwgYWVzKHggPWBUb3RhbCBDbGFpbSBCaW5gLCB5ID0gYEN1c3RvbWVyIExpZmV0aW1lIFZhbHVlYCkpICsNCiAgZ2VvbV9ib3hwbG90KCkgKyANCiAgeGxhYigiQ2xhaW0gYmluIG9mIGN1c3RvbWVycyIpICsgeWxhYigiQ0xWIikNCmBgYA0KDQpgYGB7cn0NCmFnZ3JlZ2F0ZSggYEN1c3RvbWVyIExpZmV0aW1lIFZhbHVlYCB+IGBUb3RhbCBDbGFpbSBCaW5gLCBkYXRhLCBtZWFuKQ0KYGBgDQpDcmVhdGluZyBiaW5zIGZvciBpbmNvbWUsIGRvZXNuJ3Qgc2hvdyBhbnkgY2hhbmdlcyBhY3Jvc3MgYmlucy4gQnV0IGZvciBNb250aGx5IFByZW1pdW0gQmluIGFuZCBjbGFpbSBiaW4gd2UgY2FuIG9ic2VydmUgY2hhbmdlcyBhY3Jvc3MgY2F0ZWdvcmllcy4NCg0KIyMjIElzIHRoZXJlIGFueSBwcmVmZXJhYmxlIHBvbGljeSBmb3IgdmVyeSBoaWdoIHZhbHVlZCBjdXN0b21lcnM/DQpgYGB7cn0NCnN1YnNldChkYXRhLGBDdXN0b21lciBMaWZldGltZSBWYWx1ZWA+NTAwMDApDQpgYGANCg0KUGVyc29uYWwgQXV0byBwb2xpY3kgdHlwZSBpcyB0aGUgcHJlZmVyYWJsZSBwb2xpY3kgZm9yIHZlcnkgaGlnaCBsaWZldGltZSB2YWx1ZSBjdXN0b21lcnMuDQoNCiMjIyBBcmUgYWdlbnRzIG1vcmUgZWZmZWN0aXZlIHdpdGggcmVnYXJkcyB0byBwdXJjaGFzZSBvZiBwb2xpY3kgcGxhbnM/DQpgYGB7cn0NCmRmXzE9c3Vic2V0KGRhdGEsIChSZXNwb25zZT09IlllcyIpICkNCmFnZ3JlZ2F0ZShDdXN0b21lciB+IGBTYWxlcyBDaGFubmVsYCxkZl8xLCBGVU4gPSBsZW5ndGgpDQpgYGANCg0KYGBge3J9DQoNCmFnZ3JlZ2F0ZShDdXN0b21lciB+IGBTYWxlcyBDaGFubmVsYCxkYXRhLCBGVU4gPSBsZW5ndGgpDQpgYGANCjAuMTklLDAuMTElLDAuMTElLDAuMTIlIGFyZSB0aGUgcmVzcGVjdGl2ZSBwZXJjZW50YWdlcyBvZiBwZW9wbGUgd2hvIHJlc3BvbmRlZCB3aXRoIHllcyBmcm9tIGRpZmZlcmVudCBzYWxlcyBjaGFubmVsIHcuci50IHRvdGFsIGN1c3RvbWVycyBlYWNoIGNoYW5uZWwgYnJvdWdodC5UaGlzIHNob3dzIHRoYXQgcGVvcGxlIGFyZSBtb3JlIGxpa2VseSB0byByZXNwb25kIHRvIGFnZW50cyByYXRoZXIgdGhhbiBvdGhlciBzYWxlcyBjaGFubmVsIG1vZGUuDQoNCiMjIyBDYW4gd2UgdXNlIGtub3dsZWRnZSBvZiBnZW5kZXIgYXMgbGV2ZXJhZ2UgaW4gYW55IGNhc2U/DQoNCmBgYHtyfQ0Kc3Vic2V0KGRhdGEsIChSZXNwb25zZT09IlllcyIpJiAoYEN1c3RvbWVyIExpZmV0aW1lIFZhbHVlYD4yNTAwMCkpDQoNCmBgYA0KQWZ0ZXIgb2JzZXJ2aW5nIGdyYXBocyBpdCBhcHBlYXJlZCB0aGF0IGdlbmRlciBoYXMgbm8gcm9sZSB0byBwbGF5LCBidXQgd2Ugb2JzZXJ2ZSB0aGF0IGFsbCBoaWdoIHZhbHVlZCBjdXN0b21lcnMgd2hvIGdhdmUgeWVzIGFzIHJlc3BvbnNlIGZvciANCnBvbGljeSByZW5ld2FsIGFyZSBmZW1hbGVzLg0KU28sIHdlIHNob3VsZCBmb2N1cyBtb3JlIG9uIGhpZ2ggdmFsdWVkIGZlbWFsZSBjdXN0b21lcnMgd2hvIGdhdmUgbm8gYXMgcmVzcG9uc2UsIGFzIHRoZXkgaGF2ZSBtb3JlIGNoYW5jZSB0byBzYXkgeWVzLg0KUGVvcGxlIHByZWZlciB0byB0YWtlIDIgcG9saWNpZXMgaW4gZ2VuZXJhbCwgc28gd2UgY2FuIHVzZSBpdCB0byBvdXIgbGV2ZXJhZ2UuDQoNCiMjIyBCYXNlZCBvbiBFREEsIHdoaWNoIHZhcmlhYmxlcyBhcmUgbm90IGltcG9ydGFudCBmb3IgcHJlZGljdGlvbj8NCg0KQ3VzdG9tZXIsU3RhdGUsIE1hcml0YWwgU3RhdHVzLCBFZHVjYXRpb24sR2VuZGVyLExvY2F0aW9uIGNvZGUsU2FsZXMgQ2hhbm5lbCxSZW5ldyBvZmZlciB0eXBlLFBvbGljeSxNb250aHMgU2luY2UgUG9saWN5IEluY2VwdGlvbiwgTW9udGhzIFNpbmNlIExhc3QgQ2xhaW0sSW5jb21lIGRvbid0IGNvbWUgYWNyb3NzIGFzIGltcG9ydGFudCB2YXJpYWJsZXMgZnJvbSBvdXIgZGF0YSBleHBsb3JhdGlvbi5Nb250aGx5IFByZW1pdW0gQXV0byBpcyBjb2xsaW5lYXIgd2l0aCBUb3RhbCBDbGFpbSBBbW91bnQsIHNvIHdlIG5lZWQgdG8gZHJvcCBvbmUgb2YgdGhlc2UgdmFyaWFibGVzLg0KDQojIyMgQmFzZWQgb24gRURBLCB3aGljaCB2YXJpYWJsZXMgYXJlIGltcG9ydGFudCBmb3IgcHJlZGljdGlvbj8NClJlc3BvbnNlLENvdmVyYWdlLEVtcGxveW1lbnRTdGF0dXMsVmVoaWNsZSBjbGFzcyxQb2xpY3kgdHlwZSxUb3RhbCBDbGFpbSBBbW91bnQgYXBwZWFyIHRvIGJlIGltcG9ydGFudCB2YXJpYWJsZXMuDQoNCiMjIE11bHRpdmFyaWF0ZSBBbmFseXNpcw0KDQpgYGB7cn0NCiNpbnN0YWxsLnBhY2thZ2VzKCJSQ29sb3JCcmV3ZXIiKQ0KbGlicmFyeShSQ29sb3JCcmV3ZXIpDQoNCkNMVl9UeXBlIDwtIGdncGxvdChkYXRhLCBhZXMoeD1gTnVtYmVyIG9mIFBvbGljaWVzYCwgeT1gQ3VzdG9tZXIgTGlmZXRpbWUgVmFsdWVgLCBmaWxsID0gYFNhbGVzIENoYW5uZWxgICkpKw0KICAgICAgICBnZW9tX2NvbChwb3NpdGlvbj0iZG9kZ2UiKSArIHhsYWIoIk5VTUJFUiBPRiBQT0xJQ0lFUyIpICsgeWxhYigiQ3VzdG9tZXIgTGlmZXRpbWUgVmFsdWUiKSArDQogICAgICAgIGdndGl0bGUoIkN1c3RvbWVyIExpZmV0aW1lIFZhbHVlIGJ5IFNhbGVzIENoYW5uZWwgYW5kIFBvbGljeSIpICsNCiAgICAgICAgIHNjYWxlX2ZpbGxfYnJld2VyKHBhbGV0dGUgPSAiUGFpcmVkIikNCkNMVl9UeXBlDQoNCg0KYGBgDQoNCg0KVGhlIEF2ZXJhZ2UgbnVtYmVyIG9mIHBvbGljaWVzIHRoYXQgdGhlIENvbXBhbnkgaXNzdWVzIGNvbWVzIHRvIGJlIGFyb3VuZCAyLTMgaW4gdGhlIHRoZSBnaXZlbiB0aW1lIGZyYW1lIGFuZCBpdCBpcyBub3RpY2VhYmxlIHRoYXQgQ2FsbCBDZW50ZXJzIGZldGNoIHRoZSBtb3N0IHZhbHVhYmxlIGN1c3RvbWVyLkl0IGlzIGFsc28gdG8gYmUgbm90ZWQgdGhhdCBDdXN0b21lcnMgaGF2aW5nIGhpZ2hlciBudW1iZXIgb2YgcG9saWNpZXMgZGlyZWN0bHkgYXBwcm9hY2ggdGhlIEJyYW5jaC5IZXJlIHRoZSBQb2xpY2llcyB0byBiZSBpc3N1ZWQgdnMgdGhlIExpZmV0aW1lIFZhbHVlIG9mIGEgQ3VzdG9tZXIgY291bGQgYmUgYSB0cmFkZSBvZmYsIHNpbmNlIG91ciBmb2N1cyBpcyBMaWZldGltZSBWYWx1ZSB3ZSBzaG91bGQgYmUgZm9jdXNpbmcgb24gYXR0cmFjdGluZyBtb3JlIGN1c3RvbWVycyB0aHJvdWdoIENhbGwgY2VudGVycy4NCg0KYGBge3Igd2FybmluZyA9RkFMU0V9DQpDbGFpbV9UeXBlIDwtIGdncGxvdChkYXRhLCBhZXMoeD1gTnVtYmVyIG9mIFBvbGljaWVzYCwgeT1kYXRhJGBUb3RhbCBDbGFpbSBBbW91bnRgLCBmaWxsID1gUG9saWN5IFR5cGVgICkpKw0KICAgICAgICBnZW9tX2NvbChwb3NpdGlvbj0iZG9kZ2UiKSArIHhsYWIoIk5VTUJFUiBPRiBQT0xJQ0lFUyIpICsgeWxhYigiVG90YWwgQ2xhaW0gQW10IikgKw0KICAgICAgICBnZ3RpdGxlKCJUb3RhbCBDbGFpbSBieSBuby5vZiBwb2xpY3kgYW5kIFBvbGljeSB0eXBlIikgKw0KICAgICAgICAgc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZSA9ICJQYWlyZWQiKQ0KQ2xhaW1fVHlwZQ0KYGBgDQoNCg0KSXQgaXMgZXZpZGVudCB0aGF0IG91ciBtYWpvciBzaGFyZSBvZiBDdXN0b21lcnMgcHJvY3VyZSBQZXJzb25hbCBpbnN1cmFuY2UgYW5kIHNpbmNlIHRoZSBUb3RhbCBDbGFpbSBWYWx1ZSBkZXRlcm1pbmVzIHRoZSBMaWZldGltZSBWYWx1ZSBvZiBhIGN1c3RvbWVyIHRoZSBOby5vZiBQb2xpY3kgc29sZCBwZXIgY2F0ZWdvcnkgcGxheXMgYSBjcnVjaWFsIHJvbGUgdG8gZ2VuZXJhdGUgdmFsdWUgdG8gdGhlIGNvbXBhbnkuDQoNCmBgYHtyfQ0KbG9jPC1jb3VudChkYXRhLGBMb2NhdGlvbiBDb2RlYCxDb3ZlcmFnZSkNCiNWaWV3KGxvYykNCg0KDQpnZ3Bsb3QoZGF0YSA9IGRhdGEsIGFlcyh4ID0gYExvY2F0aW9uIENvZGVgICwgeSA9YE1vbnRobHkgUHJlbWl1bSBBdXRvYCAsIGNvbG9yID0gQ292ZXJhZ2UpKSArDQogIGdlb21fYm94cGxvdCgpICsgDQogIHhsYWIoIkxvY2F0aW9uIikgKyB5bGFiKCJNb250aGx5IFByZW1pdW0iKQ0KDQpgYGANCg0KDQpUaGlzIHRvIHN0cmF0ZWdpemUgdGhlIFBsYW4gdG8gYmUgcHJvbW90ZWQgaW4gZGlmZmVyZW50IExvY2F0aW9ucywgc2luY2Ugd2Uga25vdyBtb3N0IG9mIG91ciBDdXN0b21lcnMgY29tZSBmcm9tIFN1Yi1VcmJhbiBsb2NhdGlvbnMsIHdoZXJlIHRoZSBQcmVtaXVtIGNvdmVyYWdlIG51bWJlcnMgYXJlIGhpZ2ggaXQgaXMgYWxzbyBzZWVuIHRoYXQgUHJlbWl1bSBjb3ZlcmFnZSBpcyBwcmVmZXJyZWQgYnkgbW9zdCBpcnJlc3BlY3RpdmUgb2YgdGhlIGxvY2F0aW9uLkhlbmNlIHByaW9yaXRpemluZyBQcmVtaXVtIEN1c3RvbWVycyBjYW4gYmVuZWZpdCB0aGUgYnVzaW5lc3MuDQoNCg0KDQpgYGB7ciBmaWcuYWxpZ249ImNlbnRlciIsIGVjaG8gPSBGQUxTRSxmaWcud2lkdGggPSAxNH0NCg0KY29tcGxhaW50X2NvdW50PC1jb3VudChkYXRhLGBQb2xpY3kgVHlwZWAsYE51bWJlciBvZiBPcGVuIENvbXBsYWludHNgLGBTYWxlcyBDaGFubmVsYCkNCiNjb21wbGFpbnRfY291bnQNCmdncGxvdChkYXRhID0gY29tcGxhaW50X2NvdW50LCBhZXMoeCA9IGBQb2xpY3kgVHlwZWAgLCB5ID0gbixjb2xvcj1gTnVtYmVyIG9mIE9wZW4gQ29tcGxhaW50c2AgKSkgK3NjYWxlX2ZpbGxfYnJld2VyKCJQYWlyZWQiKSsNCiAgZ2VvbV9jb2wocG9zaXRpb249ImRvZGdlIikgKyANCiAgIGZhY2V0X2dyaWQoYFNhbGVzIENoYW5uZWxgfiAuICkrDQogICBnZW9tX3RleHQoYWVzKGxhYmVsID0gbiksIHZqdXN0ID0gLTAuMiwgc2l6ZSA9IDYsDQogICAgICAgICAgICBwb3NpdGlvbiA9IHBvc2l0aW9uX2RvZGdlKDAuOSkpKw0KICB4bGFiKCJQb2xpY3kgVHlwZSIpICsgeWxhYigiTm8ub2YgT3BlbiBDb21wbGFpbnRzIikNCmBgYA0KDQoNCg0KTm8ub2YgQ29tcGxhaW50cyBjYW4gcmVmbGVjdCB1cG9uIHJlbGF0aW9uc2hpcCBvZiB0aGUgQ3VzdG9tZXIgd2l0aCB0aGUgQ29tcGFueSBnaXZlbiB0aGF0IHRoZSBzZXJ2aWNlcyBhcmUgbm90IG1hbmFnZWQgd2VsbCBieSB0aGUgY29tcGFueS5Nb3N0IG9mIG91ciBwb2xpY2llcyBhcmUgZm9yIFBlcnNvbmFsIHB1cnBvc2UgYW5kIGFyZSBtYXJrZXRlZCBieSBDYWxsLUNlbnRlci5QZXJzb25hbCBwb2xpY2llcyBiZWluZyBvdXIgZm9jYWwgcG9pbnQgd2Ugc2VlIHRoYXQgdGhvc2UgcG9saWNpZXMgZGlzdHJpYnV0ZWQgYnkgQWdlbnRzIGhhdmUgbWFqb3IgbnVtYmVyIG9mIENvbXBsYWludHMgd2hpY2ggaW5kaWNhdGVzIHBvb3Itam9iICBieSB0aGUgQWdlbnRzIGFuZCBkZWxheWVkIHJlc3BvbnNlIGJ5IHRoZSBjb21wYW55IHRvIHRoZSBUYXJnZXQgY3VzdG9tZXJzLg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KPGg0Pk1vZGVsbGluZzwvaDQ+DQoNCjxoMz5MaW5lYXIgUmVncmVzc2lvbjwvaDM+DQoNCg0KTGluZWFyIFJlZ3Jlc3Npb24gaXMgdGhlIG9sZGVzdCwgc2ltcGxlIGFuZCB3aWRlbHkgdXNlZCBzdXBlcnZpc2VkIG1hY2hpbmUgbGVhcm5pbmcgYWxnb3JpdGhtIGZvciBwcmVkaWN0aXZlIGFuYWx5c2lzLg0KSXQgaXMgYSBtZXRob2QgdG8gcHJlZGljdCBhIHRhcmdldCB2YXJpYWJsZShZKSBieSBmaXR0aW5nIHRoZSBiZXN0IGxpbmVhciByZWxhdGlvbnNoaXAgYmV0d2VlbiB0aGUgZGVwZW5kZW50KFkpIGFuZCBpbmRlcGVuZGVudCB2YXJpYWJsZShYKS4NCkl0IGhlbHBzIGRldGVybWluZToNCg0KID4+SWYgYSBpbmRlcGVuZGVudCB2YXJpYWJsZSBkb2VzIGEgZ29vZCBqb2IgaW4gcHJlZGljdGluZyB0aGUgZGVwZW5kZW50IHZhcmlhYmxlLg0KDQo+PldoaWNoIGluZGVwZW5kZW50IHZhcmlhYmxlIHBsYXlzIGEgc2lnbmlmaWNhbnQgcm9sZSBpbiBwcmVkaWN0aW5nIHRoZSBkZXBlbmRlbnQgdmFyaWFibGUuDQoNCkluIG91ciBhbmFseXNpcywgd2UgaWRlbnRpZmllZCB0aGUgdGFyZ2V0IHZhcmlhYmxlIHRvIGJlIHByZWRpY3RlZCBhcyBDdXN0b21lciBMaWZldGltZSBWYWx1ZSBhbmQgYWxsIHRoZSBvdGhlcnMgYXMgZGVwZW5kZW50IHZhcmlhYmxlIHRvIHJ1biANCmEgbXVsdGlwbGUgcmVncmVzc2lvbiBtb2RlbC4NCg0KPGgzPkZhY3RvcmlzYXRpb248L2gzPg0KDQpGYWN0b3IgaW4gUiBpcyBhIHZhcmlhYmxlIHVzZWQgdG8gY2F0ZWdvcml6ZSBhbmQgc3RvcmUgdGhlIGRhdGEsIGhhdmluZyBhIGxpbWl0ZWQgbnVtYmVyIG9mIGRpZmZlcmVudCB2YWx1ZXMuIA0KSXQgc3RvcmVzIHRoZSBkYXRhIGFzIGEgdmVjdG9yIG9mIGludGVnZXIgdmFsdWVzLiANCg0KV2UgY29udmVydGVkIGFsbCB0aGUgY2F0ZWdvcmljYWwgdmFyaWFibGVzIGFuZCAyIGRpc2NyZXRlIG51bWVyaWMgY29sdW1ucyhOdW1iZXIgb2YgcG9saWNpZXMgYW5kIG51bWJlciBvZiBvcGVuIGNvbXBsYWludHMpDQppbnRvIGZhY3RvcnMgdG8gZ2l2ZSBlYWNoIGNhdGVnb3J5IGEgbGV2ZWwgdG8gaGVscCB0aGUgcmVncmVzc2lvbiBhbmFseXNpcy4gDQoNCjxoMz5UcmFpbi1UZXN0IFNwbGl0PC9oMz4NCg0KVGhlIHRyYWluLXRlc3Qgc3BsaXQgaXMgYSB0ZWNobmlxdWUgZm9yIGV2YWx1YXRpbmcgdGhlIHBlcmZvcm1hbmNlIG9mIHRoZSBtb2RlbA0KDQo+PiBUcmFpbiBEYXRhc2V0OiBVc2VkIHRvIGZpdCB0aGUgbWFjaGluZSBsZWFybmluZyBtb2RlbC4NCg0KPj5UZXN0IERhdGFzZXQ6IFVzZWQgdG8gZXZhbHVhdGUgdGhlIGZpdCBtYWNoaW5lIGxlYXJuaW5nIG1vZGVsLg0KDQpUaGUgb2JqZWN0aXZlIGlzIHRvIGVzdGltYXRlIHRoZSBwZXJmb3JtYW5jZSBvZiB0aGUgbW9kZWwgb24gbmV3IGRhdGE6IGRhdGEgbm90IHVzZWQgdG8gdHJhaW4gdGhlIG1vZGVsLg0KDQpIZXJlLCB0aGUgZGF0YSBpcyByYW5kb21seSBzcGxpdCBpbiB0aGUgcmF0aW8gb2YgNzozIHdoZXJlIHRyYWluaW5nIGRhdGEgY29uc3RpdHV0ZXMgNzAlIG9mIHRoZSBkYXRhIGFuZCB0ZXN0aW5nIGRhdGEgaXMgMzAlIG9mIHRoZSBjb21wbGV0ZSBkYXRhc2V0Lg0KYGBge3J9DQoNCiNMUiBUSUxMIFRSQUlOIFRFU1QgU1BMSVQNCmRhdGEkYEN1c3RvbWVyIExpZmV0aW1lIFZhbHVlYD1sb2coZGF0YSRgQ3VzdG9tZXIgTGlmZXRpbWUgVmFsdWVgKQ0Kc2V0LnNlZWQoMTIzKQ0Kc2FtcGxlIDwtIHNhbXBsZShjKFRSVUUsIEZBTFNFKSwgbnJvdyhkYXRhKSwgcmVwbGFjZSA9IFQsIHByb2IgPSBjKDAuNywwLjMpKQ0KdHJhaW4gPC0gZGF0YVtzYW1wbGUsIF0NCnRlc3QgPC0gZGF0YVshc2FtcGxlLCBdDQoNCmBgYA0KDQoNCjxoMz5BcHByb2FjaDwvaDM+DQoNCjEuIFdlIGluaXRpYWxseSBidWlsdCBhIGJhc2UgbW9kZWwgd2l0aCBhbGwgdGhlIHZhcmlhYmxlcyBhbmQgZ290IFJeMiB2YWx1ZSBvZiBhcHByb3hpbWF0ZWx5IDAuNjQuIFdoZW4gd2UgY29uc3RydWN0ZWQgYSBncmFwaCBvZiBSZXNpZHVhbCB2L3MgRml0dGVkDQogICB0aGUgZ3JhcGggd2FzIGZ1bm5lbCBzaGFwZWQgaW5kaWNhdGluZyBoZXRlcm9zY2VkYXN0aWNpdHkgd2hpY2ggaXMgYWdhaW5zdCB0aGUgbGluZWFyIHJlZ3Jlc3Npb24gYXNzdW1wdGlvbnMuIFRoaXMgd2FzIGR1ZSB0byB0aGUgc2tld25lc3MgaW4gdGhlIHRhcmdldA0KICAgdmFyaWFibGUuIEhlbmNlIHdlIGxvZyB0cmFuc2Zvcm1lZCB0aGUgdGFyZ2V0IHZhcmlhYmxlLg0KDQoNCjIuIFVzaW5nIHRoZSBsb2cgdHJhbnNmb3JtZWQgdGFyZ2V0IGNvbHVtbiB3aGVuIHdlIGJ1aWx0IGEgbW9kZWwsIHRoZSBzY29yZSBqdW1wZWQgdXAgdG8gMC44OTYuIEJ1dCB0aGUgbnVtYmVyIG9mIGRlcGVuZGVudCB2YXJpYWJsZXMgdXNlZCB3ZXJlIDIyLg0KICAgW0hvd2V2ZXIsIHRoZSBjdXN0b21lciBpZCBjb2x1bW4gd2FzIGluaXRpYWxseSBkcm9wcGVkIGFzIGl0IGlzIG9mIG5vIGltcG9ydGFuY2VdIEluIG9yZGVyIHRvIG9wdGltaXplIHRoZSB1c2FnZSBvZiAyMiBkZXBlbmRlbnQgdmFyaWFibGVzLCB3ZSBkaWQgDQogICB2YXJpb3VzIHRyaWFsIGFuZCBlcnJvciBtZXRob2RzIHRvIGdldCBtYXhpbXVtIGluZm9ybWF0aW9uIGZyb20gbWluaW11bSBjb2x1bW5zLg0KDQozLiBTdGF0aXN0aWNhbCB0ZXN0cyBsaWtlIGNvcnJlbGF0aW9uLCBLcnVza2FsIFdhbGxpcyB0ZXN0cyB3ZXJlIGFsc28gcGVyZm9ybWVkIHRvIHNlZSB0aGUgZGVwZW5kZW5jZSBvZiBlYWNoIGluZGVwZW5kZW50IHZhcmlhYmxlIGZvciB0aGUgcHJlZGljdGlvbiBvZiBDTFYuDQoNCg0KNC4gSG93ZXZlciwgd2UgZmluYWxseSBjYW1lIHVwIHdpdGggdGhlIGVmZmljaWVudCBtb2RlbCB1c2luZyBhbm92YSBmb3IgZmVhdHVyZSBpbXBvcnRhbmNlLg0KDQoNCjxoMz4gRmVhdHVyZSBTZWxlY3Rpb248L2gzPg0KDQpUaGUgbWFpbiBhaW0gb2YgYSByZWdyZXNzaW9uIGFuYWx5c2lzIGlzIHRvIGZldGNoIGFzIG11Y2ggaW5mb3JtYXRpb24gYXMgcG9zc2libGUgd2l0aCB0aGUgbGVhc3QgbnVtYmVyIG9mIHZhcmlhYmxlcyB3aGljaCBhcmUgDQpzaWduaWZpY2FudC5UbyBhY2hpZXZlIHRoaXMgd2UgbmVlZCB0byBzZWxlY3QgdGhlIG1vc3Qgc2lnbmlmaWNhbnQgaW5kZXBlbmRlbnQgdmFyaWJsZXMgdGhhdCBjYW4gZXhwbGFpbiB0aGUgdmFyaWF0aW9uIG9mIHRoZSBkZXBlbmRlbnQNCnZhcmlhYmxlLg0KRmVhdHVyZSBzZWxlY3Rpb24gd2FzIHBlcmZvcm1lZCB1c2luZyBhIHN0YXRpc3RpY2FsIHRlc3QgY2FsbGVkIEFOT1ZBLg0KQU5PVkEgaXMgYSBzdGF0aXN0aWNhbCB0ZXN0IGZvciBlc3RpbWF0aW5nIGhvdyBhIHF1YW50aXRhdGl2ZSBkZXBlbmRlbnQgdmFyaWFibGUgY2hhbmdlcyBhY2NvcmRpbmcgdG8gdGhlIGxldmVscyBvZiBvbmUgb3IgbW9yZSANCmNhdGVnb3JpY2FsIGluZGVwZW5kZW50IHZhcmlhYmxlcw0KDQoNCmBgYHtyfQ0KI0FOT1ZBDQojaW5zdGFsbC5wYWNrYWdlcygnQUlDY21vZGF2ZycpDQojbGlicmFyeShBSUNjbW9kYXZnKQ0KDQphbm92YSA8LSBhb3YoYEN1c3RvbWVyIExpZmV0aW1lIFZhbHVlYCB+DQogICAgICAgICAgICAgICAgIFN0YXRlK1Jlc3BvbnNlK0NvdmVyYWdlICsNCiAgICAgICAgICAgICAgICAgRWR1Y2F0aW9uK2BFZmZlY3RpdmUgVG8gRGF0ZWAgK0VtcGxveW1lbnRTdGF0dXMrR2VuZGVyKw0KICAgICAgICAgICAgICAgICBJbmNvbWUrYExvY2F0aW9uIENvZGVgK2BNb250aHMgU2luY2UgUG9saWN5IEluY2VwdGlvbmArDQogICAgICAgICAgICAgICAgYE1hcml0YWwgU3RhdHVzYCsgYE1vbnRocyBTaW5jZSBMYXN0IENsYWltYCtgUG9saWN5IFR5cGVgKw0KICAgICAgICAgICAgICAgICBQb2xpY3krYFNhbGVzIENoYW5uZWxgKw0KICAgICAgICAgICAgICAgICBgUmVuZXcgT2ZmZXIgVHlwZWAgK2BWZWhpY2xlIFNpemVgKw0KICAgICAgICAgICAgICAgICBgTW9udGhseSBQcmVtaXVtIEF1dG9gKw0KICAgICAgICAgICAgICAgICBgTnVtYmVyIG9mIE9wZW4gQ29tcGxhaW50c2ArDQogICAgICAgICAgICAgICAgIGBOdW1iZXIgb2YgUG9saWNpZXNgKyAgDQogICAgICAgICAgICAgICAgIGBUb3RhbCBDbGFpbSBBbW91bnRgKw0KICAgICAgICAgICAgICAgICBgVmVoaWNsZSBDbGFzc2AsIGRhdGEgPSBkYXRhKQ0KDQpzdW1tYXJ5KGFub3ZhKQ0KDQoNCg0KYGBgDQpGcm9tIHRoZSByZXN1bHRzIG9idGF0aW5lZCBmcm9tIEFOT1ZBIHdlIHNlbGVjdGVkIDkgdmFyaWFibGVzIHRvIGJlIHNpZ25pZmljYW50IHdpdGggdGhlIEN1c3RvbWVyIExpZmV0aW1lIFZhbHVlIHZhcmlhYmxlIGJhc2VkIG9uIA0KdGhlaXIgcCB2YWx1ZXMuIEFsbCB0aGUgdmFyaWFibGVzIHNpZ25pZmljYW50IGF0IDAuMSBsZXZlbCBvZiBzaWduaWZpY2FuY2UgYW5kIGJlbG93IHdlcmUgY29uc2lkZXJlZCBmb3IgYnVpbGRpbmcgdGhlIG1vZGVsLiBOYW1lbHksDQpDb3ZlcmFnZSxFZHVjYXRpb24sIEVmZmVjdGl2ZSBUbyBEYXRlLCBFbXBsb3ltZW50U3RhdHVzLCBQb2xpY3ksUmVuZXcgT2ZmZXIgVHlwZSxNb250aGx5IFByZW1pdW0gQXV0byxWZWhpY2xlIENsYXNzLE51bWJlciBvZiBPcGVuIENvbXBsYWludHMsDQpOdW1iZXIgb2YgUG9saWNpZXMuDQpIb3dldmVyIHdlIGNvbnNpZGVyZWQgUG9saWN5IG92ZXIgUG9saWN5IHR5cGUgYXMgdGhleSBib3RoIGV4cGxhaW4gc2ltaWxhciB0aGluZ3MuIFdlIGFsc28gbmVnbGVjdGVkIEVmZmVjdGl2ZSBUbyBEYXRlIGNvbHVtbiBhcyB0aGUNCnJlc3VsdHMgZGlkIG5vdCBhZmZlY3QgbXVjaCB3aXRoIGl0Lg0KQWxzbywgd2UgaGF2ZSBub3QgdXNlZCB0aGUgZGVyaXZlZCBjb2x1bW4gIlByZXNlbnQgVmFsdWUgb2YgQ3VzdG9tZXIiIGFzIG9ubHkgTW9udGhseSBQcmVtaXVtIEF1dG8gd2FzIGNvbnNpZGVyZWQgc2lnbmlmaWNhbnQgd2hlcmVhcyBvdGhlciB2YXJpYWJsZXMgdXNlZCB3ZXJlbid0Lg0KVGhlIFJeMiB2YWx1ZSBkcm9wcGVkIHdoZW4gdGhlIGRlcml2ZWQgY29sdW1uIHdhcyB1c2VkLg0KDQoNCjwvaDQ+TW9kZWwgQnVpbGRpbmcgPC9oND4NCg0KVXNpbmcgdGhlIGltcG9ydGFudCB2YXJpYWJsZXMgb2J0YWluZWQgZnJvbSBBTk9WQSB3ZSB0cmFpbmVkIGEgbGluZWFyIHJlZ3Jlc3Npb24gbW9kZWwgdXNpbmcgbG0gZnVuY3Rpb24gb24gdGhlIHRyYWluaW5nIGRhdGEuDQoNCg0KIDxoMz5BY2N1cmFjeSBNZWFzdXJlczwvaDM+DQoNCmEpIFJeMiB2YWx1ZS0gSXQgcmVwcmVzZW50cyB0aGUgcHJvcG9ydGlvbiBvZiB2YXJpYW5jZSBleHBsYWluZWQgYW5kIGFsd2F5cyB0YWtlcyBvbiBhIHZhbHVlIGJldHdlZW4gMCBhbmQgMS4gDQpUaGUgaGlnaGVyIHRoZSB2YWx1ZSwgYmV0dGVyIHRoZSBtb2RlbC4gSXQgaXMgaW5kZXBlbmRlbnQgb2YgdGhlIHNjYWxlIG9mIFkuDQoNCmIpIFJNU0UgLSBSb290IE1lYW4gU3F1YXJlIEVycm9yIChSTVNFKSBpcyB0aGUgc3RhbmRhcmQgZGV2aWF0aW9uIG9mIHRoZSByZXNpZHVhbHMgKHByZWRpY3Rpb24gZXJyb3JzKS4gUmVzaWR1YWxzIGFyZSBhIG1lYXN1cmUgb2YgaG93IGZhciBmcm9tIHRoZQ0KICAgcmVncmVzc2lvbiBsaW5lIGRhdGEgcG9pbnRzIGFyZS4gUk1TRSBpcyBhIG1lYXN1cmUgb2YgaG93IHNwcmVhZCBvdXQgdGhlc2UgcmVzaWR1YWxzIGFyZS4gSW4gb3RoZXIgd29yZHMsIGl0IHRlbGxzIHlvdSBob3cgY29uY2VudHJhdGVkIHRoZSBkYXRhIGlzDQogICBhcm91bmQgdGhlIGxpbmUgb2YgYmVzdCBmaXQuDQoNCg0KDQoNCg0KYGBge3IgaW5jbHVkZSA9RkFMU0V9DQoNCg0KDQpmaXRfbG9nPC0gbG0oYEN1c3RvbWVyIExpZmV0aW1lIFZhbHVlYCB+DQogICAgICAgICAgICAgQ292ZXJhZ2UrDQogICAgICAgICAgICAgRWR1Y2F0aW9uKw0KICAgICAgICAgICAgIEVtcGxveW1lbnRTdGF0dXMrDQogICAgICAgICAgICBgUG9saWN5YCsNCiAgICAgICAgICAgICBgUmVuZXcgT2ZmZXIgVHlwZWArDQogICAgICAgICAgICAgYE1vbnRobHkgUHJlbWl1bSBBdXRvYCsNCiAgICAgICAgICAgICBgTnVtYmVyIG9mIE9wZW4gQ29tcGxhaW50c2ArDQogICAgICAgICAgICAgYE51bWJlciBvZiBQb2xpY2llc2ArDQogICAgICAgICAgICAgYFZlaGljbGUgQ2xhc3NgLA0KICAgICAgICAgICAgIGRhdGE9dHJhaW4pDQpzdW1tYXJ5KGZpdF9sb2cpDQoNCmBgYA0KDQpgYGB7cn0NCnJlc3VsdCA9IGJyb29tOjpnbGFuY2UoZml0X2xvZykNCnJlc3VsdA0KDQoNCg0KYGBgDQpgYGB7ciB3YXJuaW5nPUZBTFNFfQ0KI2luc3RhbGwucGFja2FnZXMoIm1vZGVsciIpDQpsaWJyYXJ5KG1vZGVscikNCiNpbnN0YWxsLnBhY2thZ2VzKCJicm9vbSIpIyBwcm92aWRlcyBlYXN5IHBpcGVsaW5lIG1vZGVsaW5nIGZ1bmN0aW9ucw0KbGlicmFyeShicm9vbSkgDQojaW5zdGFsbC5wYWNrYWdlcygiTWV0cmljcyIpDQpsaWJyYXJ5KE1ldHJpY3MpDQpsaWJyYXJ5KGRwbHlyKQ0KI2luc3RhbGwucGFja2FnZXMoInBia3J0ZXN0IiwgZGVwZW5kZW5jaWVzID0gVFJVRSkNCiNsaWJyYXJ5KGNhcmV0KQ0KdGVzdCAlPiUNCiAgYWRkX3ByZWRpY3Rpb25zKGZpdF9sb2cpDQogICNzdW1tYXJpc2UoTVNFID0gbWVhbigoYEN1c3RvbWVyIExpZmV0aW1lIFZhbHVlYC1wcmVkKV4yKSkNCg0KeV90cmFpbiA9IHByZWRpY3QoZml0X2xvZywgbmV3ZGF0YSA9IHRyYWluKQ0KUjIgPC0gMS0gKHN1bSgodHJhaW4kYEN1c3RvbWVyIExpZmV0aW1lIFZhbHVlYC15X3RyYWluKV4yKSAvIHN1bSgodHJhaW4kYEN1c3RvbWVyIExpZmV0aW1lIFZhbHVlYCAtIG1lYW4odHJhaW4kYEN1c3RvbWVyIExpZmV0aW1lIFZhbHVlYCkpXjIpKQ0KcHJpbnQoUjIgKiAxMDApIA0KDQpjYXJldDo6Uk1TRSh5X3RyYWluLHRyYWluJGBDdXN0b21lciBMaWZldGltZSBWYWx1ZWApDQoNCnlfdGVzdCA9IHByZWRpY3QoZml0X2xvZywgbmV3ZGF0YSA9IHRlc3QpDQpSMyA8LSAxLSAoc3VtKCh0ZXN0JGBDdXN0b21lciBMaWZldGltZSBWYWx1ZWAteV90ZXN0KV4yKSAvIHN1bSgodGVzdCRgQ3VzdG9tZXIgTGlmZXRpbWUgVmFsdWVgIC0gbWVhbih0ZXN0JGBDdXN0b21lciBMaWZldGltZSBWYWx1ZWApKV4yKSkNCnByaW50KFIzICogMTAwKSANCg0KY2FyZXQ6OlJNU0UoeV90ZXN0LHRlc3QkYEN1c3RvbWVyIExpZmV0aW1lIFZhbHVlYCkNCmBgYA0KDQpBZnRlciB0cmFpbmluZyB0aGUgbW9kZWwgLCB3ZSBjYW4gZ2V0IGEgc3VtbWFyeSBvZiByZXN1bHRzIHVzaW5nIGJyb29tIHBhY2thZ2Ugd2hpY2ggZ2l2ZXMgcl4yLCBhZGogcl4yLCBSU0UgYW5kIG90aGVyIGFjY3VyYWN5IG1lYXN1cmVzIHRvIGFzc2VzIHRoZQ0KbW9kZWwgcGVyZm9ybWFjZS4NCg0KSG93ZXZlciwgd2UgYXNzZXNzZWQgdGhlIHRyYWluaW5nIGFuZCB0ZXN0aW5nIHByZWRpY3Rpb25zIGJ5IGNhbGN1bHRpbmcgdGhlIFJeIHZhbHVlIGFuZCBSTVNFIGZvciB0cmFpbmluZyBhbmQgdGVzdGluZyBkYXRhLg0KDQpUaGUgcmVzdWx0cyBvYnRhaW5lZCBhcmU6DQp0cmFpbiBkYXRhIDogUl4yPTAuODk1OTQ4MiBhbmQgUk1TRT0wLjIwOTk4MDUNCnRlc3QgZGF0YSAgOiBSXjI9MC44OTgyMDk1IGFuZCBSTVNFPTAuMjA5NjUyMw0KDQpUaGUgb2J0YWluZWQgcmVzdWx0cyB3ZXJlIHNhdGlzZmFjdG9yeSB3aXRoIGxlc3MgZXJyb3IgYW5kIGhlbmNlIGNvbmNsdWRlZCB0aGlzIHRvIGJlIHRoZSBiZXN0IGZpdCBtb2RlbCBmb3IgcHJlZGljdGlvbiBvZiB0aGUgdGFyZ2V0IA0KdmFyaWFibGUgQ3VzdG9tZXIgTGlmZXRpbWUgVmFsdWUgZnJvbSB0aGUgbW9zdCBzaWduaWZpY2FudCB2YXJpYWJsZXMuDQoNCjxoMz5Bc3N1bXB0aW9ucyBhbmQgUmVzaWR1YWwgUGxvdHMgZm9yIEFjY3VyYWN5IE1lYXN1cmU8L2gzPg0KDQoNCjxoND5Db3JyZWxhdGlvbiBvZiBFcnJvciBUZXJtczwvaDQ+DQpgYGB7cn0NCg0KZGF0YSAlPiUNCmFkZF9yZXNpZHVhbHMoZml0X2xvZykgJT4lDQogICBnZ3Bsb3QoYWVzKGRhdGEkYEN1c3RvbWVyIExpZmV0aW1lIFZhbHVlYCwgcmVzaWQpKSArDQogIGdlb21fbGluZSgpDQpgYGANCkNvcnJlbGF0aW9uIG9mIGVycm9yIHRlcm0NCkFuIGltcG9ydGFudCBhc3N1bXB0aW9uIG9mIHRoZSBsaW5lYXIgcmVncmVzc2lvbiBtb2RlbCBpcyB0aGF0IHRoZSBlcnJvciB0ZXJtcyAgYXJlIHVuY29ycmVsYXRlZC4gVGhpcyBzY2F0dGVycGxvdCBpcyB1c2VkIHRvIGRldGVjdCBhIHBhcnRpY3VsYXIgDQpmb3JtIG9mIG5vbi1pbmRlcGVuZGVuY2Ugb2YgdGhlIGVycm9yIHRlcm1zLCBuYW1lbHkgc2VyaWFsIGNvcnJlbGF0aW9uLiAgQSBSZXNpZHVhbCB2cy4gb3JkZXIgcGxvdCBoZWxwcyB0byBzZWUgaWYgdGhlcmUgaXMgYW55IGNvcnJlbGF0aW9uIGJldHdlZW4gdGhlIGVycm9yIHRlcm1zDQp0aGF0IGFyZSBuZWFyIGVhY2ggb3RoZXIgaW4gdGhlIHNlcXVlbmNlLkhvd2V2ZXIsIGlmIHdlIGxvb2sgYXQgb3VyIG1vZGVsw6LigqzihKJzIHJlc2lkdWFscyB3ZSBzZWUgdGhhdCBhZGphY2VudCByZXNpZHVhbHMgZG8gbm90IHRlbmQgdG8gdGFrZSBvbiBzaW1pbGFyIHZhbHVlcywgaGVuY2UgZXJyb3INCnRlcm1zIGFyZSBub3QgY29ycmVsYXRlZC4NCg0KPGg0PkRldGVjdGluZyBNdWx0aWNvbGxpbmVhcml0eSB1c2luZyBHVklGPC9oND4NCmBgYHtyfQ0KY2FyOjp2aWYoZml0X2xvZykNCmBgYA0KTXVsdGljb2xsaW5lYXJpdHkgY2FuIGJlIGFzc2Vzc2VkICBieSBjb21wdXRpbmcgdGhlIFZJRih2YXJpYW5jZSBpbmZsYXRpb24gZmFjdG9yKSB2YWx1ZS4gQW55IHZhcmlhYmxlIHdpdGggYSBoaWdoIFZJRiB2YWx1ZSBzaG91bGQgYmUgcmVtb3ZlZCBmcm9tIHRoZSBtb2RlbC4gDQpJbiBvdXIgbW9kZWwgYWxsIHRoZSB2YXJpYWJsZXMgaGF2ZSBWSUYgdmFsdWVzIGJldHdlZW4gMSBhbmQgNSB3aGljaCBzYXRpc2ZpZXMgdGhlIGNvbmRpdGlvbiwgbWVhbnMgbm8gIHNldmVyaXR5IG9mIG11bHRpY29sbGluZWFyaXR5Lg0KSGVyZSBndmlmIGlzIHRoZSBzcXVhcmUgcm9vdCBvZiB0aGUgVklGIGZvciBpbmRpdmlkdWFsIHByZWRpY3RvcnMgYW5kIHRodXMgY2FuIGJlIHVzZWQgZXF1aXZhbGVudGx5DQoNCjxoND5SZXNpZHVhbCBQbG90czwvaDQ+DQpgYGB7cn0NCnBsb3QoZml0X2xvZykNCmBgYA0KMS4gUmVzaWR1YWwgVnMgRml0dGVkIFZhbHVlcw0KSW4gdGhpcyBzY2F0dGVyIHBsb3QsIHRoZSBkaXN0cmlidXRpb24gb2YgcmVzaWR1YWxzIChlcnJvcnMpIHZzIGZpdHRlZCB2YWx1ZXMgKHByZWRpY3RlZCB2YWx1ZXMpIGlzIGRlcGljdGVkLlNpbmNlIHRoZSBwbG90IG5vdyBkb2VzIG5vdCBzaG93IGEgZnVubmVsIHNoYXBlLCBpdA0KaXMgYW4gaW5kaWNhdGlvbiBvZiBjb25zdGFudCB2YXJpYW5jZSwgaS5lLmhvbW9za2VkYXN0aWNpdHkuDQpBbHNvLCBzaW5jZSB0aGVyZSBpcyBubyByZWNvZ25pemFibGUgcGF0dGVybiBzZWVuLCBpdCBpbmRpY2F0ZXMgdGhhdCB0aGUgYXNzdW1wdGlvbiBvZiBsaW5lYXJpdHkgaXMgZmFpci4NCg0KMi4gTm9ybWFsIFEtUSBQbG90DQpUaGlzIHEtcSwgb3IgcXVhbnRpbGUtcXVhbnRpbGUsIHNjYXR0ZXIgcGxvdCBoZWxwcyBpbiB0aGUgdmFsaWRhdGlvbiBvZiB0aGUgbm9ybWFsIGRpc3RyaWJ1dGlvbiBhc3N1bXB0aW9uIGluIG91ciBkYXRhIHNldC4gV2UgY2FuIGluZmVyIGlmIHRoZSBkYXRhIGhhcyBhIG5vcm1hbCANCmRpc3RyaWJ1dGlvbiBieSBsb29raW5nIGF0IHRoaXMgZ3JhcGguIElmIHRoaXMgaXMgdGhlIGNhc2UsIHRoZSBwbG90IHdpbGwgdGVuZCB0byBiZSBmYWlybHkgc3RyYWlnaHQgbGluZS4gSW4gb3VyIGNhc2UsIHRoZXJlIGlzIHN0cm9uZyBkZXZpYXRpb24gZnJvbSB0aGUgZGlhZ29uYWwgDQpsaW5lIHdoaWNoIHNob3dzIHRoYXQgb3VyIHJlc2lkdWFscyBhcmUgbm90IG5vcm1hbGx5IGRpc3RyaWJ1dGVkLg0KDQozLiBTY2FsZS1Mb2NhdGlvbiBQbG90DQpUaGlzIHBsb3QgY2FuIGJlIHVzZWQgdG8gZGV0ZWN0IGhvbW9za2VkYXN0aWNpdHkgKGFzc3VtcHRpb24gb2YgZXF1YWwgdmFyaWFuY2UpLiBJdCBkaXNwbGF5cyBob3cgdGhlIHJlc2lkdWFscyBhcmUgZGlzdHJpYnV0ZWQgYWNyb3NzIHRoZSBwcmVkaWN0b3IgcmFuZ2UuDQpJdCdzIHNpbWlsYXIgdG8gdGhlIHJlc2lkdWFsIHZzLiBmaXR0ZWQgdmFsdWUgcGxvdCwgYnV0IGl0IGFjdHVhbGx5IHVzZXMgc3RhbmRhcmRpc2VkIHJlc2lkdWFsIHZhbHVlcy4gSGVyZSwgd2UgY2FuIHNlZSBhIGRpYWdvbmFsIGxpbmUgd2l0aCBzb21ld2hhdCBlcXVhbGx5IA0KZGlzdHJpYnV0ZWQgcG9pbnRzIHdoaWNoIHNob3dzIGxlc3MgaG9tb3NrZWRhc3RpY2l0eS4NCg0KNC4gUmVzaWR1YWxzIFZzIExldmVyYWdlIFBsb3QNClRoaXMgaXMgYWxzbyBrbm93biBhcyBDb29rw6LigqzihKJzIERpc3RhbmNlIHBsb3QuIEl0IGlzIGEgbWV0aG9kIG9mIGRldGVybWluaW5nIHdoaWNoIHBvaW50cyBoYXZlIG1vcmUgaW5mbHVlbmNlIHRoYW4gb3RoZXJzLiBTdWNoIGluZmx1ZW50aWFsIGxvY2F0aW9ucyBoYXZlIA0KYSBzaWduaWZpY2FudCBpbXBhY3Qgb24gdGhlIHJlZ3Jlc3Npb24gbGluZS4gSW4gb3VyIGNhc2UsIENvb2vDouKCrOKEonMgZGlzdGFuY2Ugc2NvcmVzIGFyZSBoaWdoIGFuZCBhcmUgY2x1c3RlcmVkIG5lYXIgdGhlIHRvcCBvZiBvdXIgbGV2ZXJhZ2UgcGxvdCwgaW5kaWNhdGluZyB0aGF0DQp0aGV5IGhhdmUgYSBzaWduaWZpY2FudCBpbmZsdWVuY2Ugb24gdGhlIHJlZ3Jlc3Npb24gcmVzdWx0cy4NCg0KDQoNCjxoMz5SYW5kb20gRm9yZXN0PC9oMz4NCmBgYHtyfQ0KDQoNCg0KDQpgYGANCg0KDQpgYGB7cn0NCiNpbnN0YWxsLnBhY2thZ2VzKCJyYW5kb21Gb3Jlc3QiKQ0KbGlicmFyeShyYW5kb21Gb3Jlc3QpDQoNCg0KI2hlYWQodHJhaW4pDQojY29sbmFtZXModHJhaW4pDQpSRl9maXQ8LSByYW5kb21Gb3Jlc3QoYEN1c3RvbWVyIExpZmV0aW1lIFZhbHVlYCB+DQogICAgICAgICAgICAgQ292ZXJhZ2UrDQogICAgICAgICAgICAgRWR1Y2F0aW9uKw0KICAgICAgICAgICAgIEVtcGxveW1lbnRTdGF0dXMrDQogICAgICAgICAgICAgdHJhaW4kYFBvbGljeWAgKw0KICAgICAgICAgICAgIHRyYWluJGBSZW5ldyBPZmZlciBUeXBlYCsNCiAgICAgICAgICAgICB0cmFpbiRgTW9udGhseSBQcmVtaXVtIEF1dG9gKw0KICAgICAgICAgICAgIHRyYWluJGBOdW1iZXIgb2YgT3BlbiBDb21wbGFpbnRzYCsNCiAgICAgICAgICAgICB0cmFpbiRgTnVtYmVyIG9mIFBvbGljaWVzYCsNCiAgICAgICAgICAgICB0cmFpbiRgVmVoaWNsZSBDbGFzc2AsDQogICAgICAgICAgICAgZGF0YT10cmFpbikNClJGX2ZpdA0KDQojY2FyZXQ6OlJNU0UoeV90ZXN0LHRlc3QkYEN1c3RvbWVyIExpZmV0aW1lIFZhbHVlYCkNCg0KYGBgDQoxLiBGcm9tIHRoZSBzdW1tYXJ5IHJlc3VsdHMgb2YgdGhlIHByZWRpY3RlZCB2YWx1ZXMsIHdoZW4gdGhlIFJhbmRvbSBGb3Jlc3QgUmVncmVzc29yIGlzIHRhc2tlZCB3aXRoIHRoZSBwcm9ibGVtIG9mIHByZWRpY3RpbmcgZm9yIHZhbHVlcyBub3QgcHJldmlvdXNseSBzZWVuLCANCiAgIGl0IHdpbGwgYWx3YXlzIHByZWRpY3QgYW4gYXZlcmFnZSBvZiB0aGUgdmFsdWVzIHNlZW4gcHJldmlvdXNseS4gT2J2aW91c2x5IHRoZSBhdmVyYWdlIG9mIGEgc2FtcGxlIGNhbiBub3QgZmFsbCBvdXRzaWRlIHRoZSBoaWdoZXN0IGFuZCBsb3dlc3QgdmFsdWVzIGluIHRoZSBzYW1wbGUuIA0KICAgVGhlIFJhbmRvbSBGb3Jlc3QgUmVncmVzc29yIGlzIHVuYWJsZSB0byBkaXNjb3ZlciB0cmVuZHMgdGhhdCB3b3VsZCBlbmFibGUgaXQgaW4gZXh0cmFwb2xhdGluZyB2YWx1ZXMgdGhhdCBmYWxsIG91dHNpZGUgdGhlIHRyYWluaW5nIHNldC4gDQogICBXaGVuIGZhY2VkIHdpdGggc3VjaCBhIHNjZW5hcmlvLCB0aGUgcmVncmVzc29yIGFzc3VtZXMgdGhhdCB0aGUgcHJlZGljdGlvbiB3aWxsIGZhbGwgY2xvc2UgdG8gdGhlIG1heGltdW0gdmFsdWUgaW4gdGhlIHRyYWluaW5nIHNldC4NCg0KMi4gVGhlIG9idGFpbmVkIHRyYWluIHNjb3JlIGlzIDk1LjczNDQyIGFuZCB0ZXN0IHNjb3JlIGlzIDg5LjgxMTQyIGZvciBSYW5kb20gRm9yZXN0LiBUaGlzIHNob3cgdGhhdCB0aGVyZSBpcyBhIHNsaWdodCBvdmVyZml0dGluZyBvZiB0aGUgbW9kZWwuDQoNCjMuIFRoZSBjb21wbGV4aXR5IG9mIFJhbmRvbSBGb3Jlc3QgbW9kZWwgaXMgaGlnaCBhcyBpdCBpcyBiYXNlZCBvbiBib290c3RyYXAgYWdncmVnYXRpb24gYW5kIGJhZ2dpbmcgdGVjaG5pcXVlcy4NCg0KDQo8aDM+U3VwcG9ydCBWZWN0b3IgTWFjaGluZTwvaDM+DQoNCmBgYHtyfQ0KI1NWTQ0KI2luc3RhbGwucGFja2FnZXMoImUxMDcxIikNCmxpYnJhcnkoZTEwNzEpDQoNCmZpdDwtIHN2bShkYXRhJGBDdXN0b21lciBMaWZldGltZSBWYWx1ZWAgfg0KICAgICAgICAgICBkYXRhJEVkdWNhdGlvbitkYXRhJGBFZmZlY3RpdmUgVG8gRGF0ZWAgK2RhdGEkRW1wbG95bWVudFN0YXR1cytkYXRhJEdlbmRlcisNCiAgICAgICAgICAgZGF0YSRJbmNvbWUrZGF0YSRgTG9jYXRpb24gQ29kZWAgK2RhdGEkYE1vbnRocyBTaW5jZSBQb2xpY3kgSW5jZXB0aW9uYCsNCiAgICAgICAgICAgZGF0YSRgTWFyaXRhbCBTdGF0dXNgKyBkYXRhJGBNb250aHMgU2luY2UgTGFzdCBDbGFpbWArZGF0YSRgUG9saWN5IFR5cGVgKw0KICAgICAgICAgICBkYXRhJGBNb250aGx5IFByZW1pdW0gQXV0b2AgKyBkYXRhJFBvbGljeSArZGF0YSRgU2FsZXMgQ2hhbm5lbGArDQogICAgICAgICAgIGRhdGEkYE51bWJlciBvZiBPcGVuIENvbXBsYWludHNgICsgZGF0YSRgTnVtYmVyIG9mIFBvbGljaWVzYCArICANCiAgICAgICAgICAgZGF0YSRgUmVuZXcgT2ZmZXIgVHlwZWAgKyBkYXRhJGBUb3RhbCBDbGFpbSBBbW91bnRgKyANCiAgICAgICAgICAgZGF0YSRgVmVoaWNsZSBDbGFzc2ArZGF0YSRgVmVoaWNsZSBTaXplYCwgZGF0YT10cmFpbikNCnN1bW1hcnkoZml0KQ0KDQoNCmBgYA0KYGBge3Igd2FybmluZz1GQUxTRX0NCnByZWRpY3RlZFlfIDwtIHByZWRpY3QoZml0LCB0cmFpbikNCmVycm9yXzIgPC0gdHJhaW4kIkN1c3RvbWVyIExpZmV0aW1lIFZhbHVlIiAtIHByZWRpY3RlZFlfDQpzdm1fZXJyb3IgPC0gc3FydChtZWFuKGVycm9yXzJeMikpIA0Kc3ZtX2Vycm9yDQpgYGANCmBgYHtyIHdhcm5pbmc9RkFMU0V9DQpwcmVkaWN0ZWRZIDwtIHByZWRpY3QoZml0LCB0ZXN0KQ0KZXJyb3JfMiA8LSB0ZXN0JGBDdXN0b21lciBMaWZldGltZSBWYWx1ZWAgLSBwcmVkaWN0ZWRZDQpzdm1fZXJyb3IgPC0gc3FydChtZWFuKGVycm9yXzJeMikpDQpzdm1fZXJyb3INCmBgYA0KDQpPbiBhcHBseWluZyBTVk0gbW9kZWwsIHdlIGdldCA3OTAwIFJNU0Ugd2hpY2ggaXMgbXVjaCBoaWdoZXIgdGhhbiB0aGUgYmFzaWMgbW9kZWwgb2YgTGluZWFyIHJlZ3Jlc3Npb24gd2hpY2ggZ2F2ZSB1cyBhcHByb3ggNDAwMCBSTVNFIC4gVGhlc2UgUk1TRSdzIGhhdmUgYmVlbiBjb21wYXJlZCB3aXRob3V0IGxvZyB0cmFuc2Zvcm1hdGlvbiwgYW5kIGlmIHdlIHdhbnQgYmV0dGVyIHByZWRpY3Rpb24gZnJvbSBTVk0gd2UgbmVlZCB0byBvcHRpbWl6ZSB0aGUgbW9kZWwgaW4gYSBiZXR0ZXIgd2F5Lg0KYGBge3Igd2FybmluZz0gRkFMU0V9DQoNCmBgYA0KDQo8aDI+UmVzdWx0cyBvZiBpbXBvcnRhbnQgdmFyaWFibGVzIGZyb20gRURBLCBBTk9WQSBhbmQgTGluZWFyIFJlZ3Jlc3Npb248L2gyPg0KDQo+PjEuIEVEQQ0KDQpSZXNwb25zZSxUb3RhbCBDbGFpbSBBbW91bnQsQ292ZXJhZ2UsRW1wbG95bWVudFN0YXR1cyxWZWhpY2xlIGNsYXNzLFBvbGljeSB0eXBlLCBNb250aGx5IFByZW1pdW0gQXV0bw0KW0R1ZSBtdWx0aWNvbGxpbmVhcml0eSBiL3cgVG90YWwgQ2xhaW0gQW1vdW50IGFuZCBNb250aGx5IFByZW1pdW0gQXV0bywgcmVqZWN0ZWQgVG90YWwgQ2xhaW0gQW1vdW50IHRvIGJlIGFuIGltcG9ydGFudCB2YXJpYWJsZS5dDQoNCj4+Mi4gQU5PVkENCg0KQ292ZXJhZ2UsRWR1Y2F0aW9uLEVtcGxveW1lbnRTdGF0dXMsIFBvbGljeSxSZW5ldyBPZmZlciBUeXBlLE1vbnRobHkgUHJlbWl1bSBBdXRvLFZlaGljbGUgQ2xhc3MsTnVtYmVyIG9mIE9wZW4gQ29tcGxhaW50cyxOdW1iZXIgb2YgUG9saWNpZXMNCg0KPj4zLiBMSU5FQVIgUkVHUkVTU0lPTg0KDQpFZHVjYXRpb24sRW1wbG95bWVudCBTdGF0dXMsIEdlbmRlciwgTW9udGhseSBwcmVtaXVtIEF1dG8sIFNhbGVzIENoYW5uZWwsIE51bWJlciBvZiBvcGVuIGNvbXBsYWludHMsIE51bWJlciBvZiBwb2xpY2llUywgVmVoaWNsZSBDbGFzcw0KDQoNCg0KDQoNCjxoMj5Db25jbHVzaW9uPC9oMj4NCg0KV2h5IENMVj8NCg0KVWx0aW1hdGVseSwgdGhlIGNvbXBhbnkganVzdCBuZWVkcyB0byBiZSBtaW5kZnVsIG9mIHRoZSB2YWx1ZSB0aGF0IGEgY3VzdG9tZXIgcHJvdmlkZXMgb3ZlciB0aGVpciBsaWZldGltZSByZWxhdGlvbnNoaXAgd2l0aCBpdC4gQnkgdW5kZXJzdGFuZGluZyB0aGUgY3VzdG9tZXJzJw0KZGV0YWlscyByZWdhcmRpbmcgdmFyaW91cyBhc3BlY3RzIGFuZCBhbmFseXppbmcgYWxsIGtleSB0b3VjaHBvaW50cywgb25lIGNhbiB1bmRlcnN0YW5kIHRoZSBrZXkgZHJpdmVycyBvZiBDTFYuIENMViBpcyBpbmRlZWQgYSBncmVhdCBtZXRyaWMgdGhhdCBzaG91bGQgYmUgdXNlZCB0bw0KaW1wcm92ZSBidXNpbmVzcyBzdHJhdGVnaWVzLg0KDQpIb3cgZ29vZCBpcyB0aGUgYW5hbHlzaXM/DQoNClBlcmZvcm1pbmcgRXhwbG9yYXRvcnkgRGF0YSBBbmFseXNpcywgU3RhdGlzdGljYWwgdGVzdHMgYW5kIHVzaW5nIFN0YXRpc3RpY2FsIG1vZGVscyBsaWtlIGxpbmVhciByZWdyZXNzaW9uIHdlIGFuYWx5c2VkIHRoZSBlZmZlY3Qgb2YgZGlmZmVyZW50IHZhcmlhYmxlcyBvbiB0aGUgdmFyaWF0aW9uDQpvZiB0aGUgdGFyZ2V0IHZhcmlhYmxlIC0gQ3VzdG9tZXIgTGlmZXRpbWUgVmFsdWUgYW5kIGFsc28gY29uY2x1ZGVkIHdoaWNoIGFyZSB0aGUgaW1wb3J0YW50IHZhcmlhYmxlcyB0aGF0ICBhcmUgc3VmZmljaWVudCB0byBnaXZlIG1heGltdW0gaW5mb3JtYXRpb24gdG8gcHJlZGljdCB0aGUgQ0xWLg0KVGhlIHJlc3VsdHMgb2J0YWluZWQgZnJvbSBhbGwgdGhlIDMgbWV0aG9kcyB3ZXJlIGFwcHJveGltYXRlbHkgc2ltaWxhciBhbmQgaGVuY2UgdGhlIGFuYWx5c2lzIHNlZW1zIHRvIGJlIGVmZmljaWVudC4NCg0KSG93IGdvb2QgaXMgdGhlIGZpbmFsIG1vZGVsPw0KDQpDb25zaWRlcmluZyBudW1iZXIgb2YgZmFjdG9ycywgd2UgY2FuIGNvbmNsdWRlIHRoYXQgTGluZWFyIFJlZ3Jlc3Npb24gbW9kZWwsIGJlaW5nIG9uZSBvZiB0aGUgc2ltcGxlc3QgbW9kZWxzIGhhcyBnaXZlbiB0aGUgYmVzdCBSXjIgdmFsdWUgb2YgMC44OSB3aXRoIGxlYXN0IGVycm9yIHJhdGUgb2YgMC4yMCBjb21wYXJlZCANCnRvIG90aGVyIG1vZGVscyBsaWtlIHJhbmRvbSBmb3Jlc3QgYW5kIFN1cHBvcnQgVmVjdG9yIE1hY2hpbmUgd2hpY2ggaGFzIGl0cyBvd24gY29ucyBvdmVyIExpbmVhciByZWdyZXNzaW9uLiANCldlIGhhdmUgb3B0aW1pemVkIHRoZSBtb2RlbCB0byBwcmVkaWN0IHRoZSBDdXN0b21lciBMaWZldGltZSBWYWx1ZSBmcm9tIDkgaW5kZXBlbmRlbnQgYW5kIGhlbmNlIGNhbiBiZSBjb25jbHVkZWQgYXMgYSBwcmV0dHkgZ29vZCBtb2RlbCBmb3IgcHJlZGljdGlvbi4NCg0KPGgyPkJ1c2luZXNzIHJlY29tbWVuZGF0aW9uczwvaDI+DQoNCkZyb20gdGhlIG1vZGVsIGFuZCB0aGUgYW5hbHlzaXMgd2UgZGVzaWduZWQsIHdlIGNhbiBzdWdnZXN0IHRoYXQ6DQoNCjEuIFRoZSBjb21wYW55IHNob3VsZCB0YXJnZXQgbWFpbmx5IHRoZSBjdXN0b21lcnMgd2hvIGFyZSBlbXBsb3llZC4NCg0KMi4gVGhlIGN1c3RvbWVycyB3aG9zZSBFZHVjYXRpb24gaXMgbWFzdGVyIGxldmVsIHNob3VsZCBiZSB0YXJnZXRlZCBtb3JlIHdoZXJlYXMgZG9jdG9ycyBkbyBub3Qgc2VydmUgdG8gYmUgdmFsdWFibGUgY3VzdG9tZXJzLg0KDQozLiBUaGUgbnVtYmVyIG9mIGNvbXBsYWludHMgc2hvdWxkIGJlIHJlZHVjZWQgYmVjYXVzZSBtb3JlIG51bWJlciBvZiBjb21wbGFpbnRzIHRoZSBjb21wYW55IGlzIG1vcmUgcHJvbmUgdG93YXJkcyBsb3NpbmcgdGhlIGN1c3RvbWVyLiBDb21wYWxpbnRzIGFib3ZlIDIgc2hvdWxkIGZvY3VzZWQuDQoNCjQuIE1vcmUgYXR0ZW50aW9uIHNob3VsZCBiZSBnaXZlbiB0byB0aGUgRXh0ZW5kZWQgYW5kIHByZW1pdW0gY3VzdG9tZXJzLg0KDQo1LiBUaGUgdGFyZ2V0IGF1ZGllbmNlIHNob3VsZCBiZSBmZW1hbGUgYXMgdGhleSBhcmUgZWFzaWVyIHRvIGNvbnZpbmNlIGFjY29yZGluZyB0byBvdXIgYW5hbHlzaXMgb2YgcmVzcG9uc2UgZ2l2ZW4gdG93YXJkcyB0aGUgcG9saWN5Lg0KDQo2LiBUaGUgY29tcGFueSBzaG91bGQgc3RhcnQgaW5jcmVhc2luZyB0aGVpciBwb2xpY3kgYWR2ZXJ0aXNlbWVudCB0aHJvdWdoIGJyYW5jaCBhbmQgYWdlbnRzIGFzIHRoZSBudW1iZXIgb2YgcG9saWN5IGFmZmVjdHMgdGhlIENMVi4NCg0KDQo8aDI+U3VnZ2VzdGlvbnM8L2gyPg0KDQoNClN1Z2dlc3RpbmcgbmV3IHZhcmlhYmxlcyB0aGF0IGNhbiBpbXByb3ZlIHRoZSBhbmFseXNpczoNCjEuIERlYnQgdG8gaW5jb21lIHJhdGlvDQoyLiBOdW1iZXIgb2YgaG91c2VzIG93bmVkDQozLiBOdW1iZXIgb2YgY2FycyBvd25lZA0KNC4gVHlwZSBvZiBwdXJjaGFzZS0gSW5zdGFsbG1lbnQgb3Igb25lLW9mZg0KNS4gUHJpY2Ugb2YgaW5zdXJlZCBjb21tb2RpdHkNCg0KDQo8aDI+Q29udHJpYnV0aW9uOjwvaDI+DQoNCkRvbmEgU2FtICAgICAgICAgICgyMEJEQTAyKSAtIEVEQSArIEFzc3VtcHRpb25zIG9mIExpbmVhciBSZWdyZXNzaW9uDQoNClByYXRoaWJoYSBLIFMgICAgICgyMEJEQTE1KSAtIEVEQSArIExpbmVhciBSZWdyZXNzaW9uICsgUmFuZG9tIEZvcmVzdCArIFJlcG9ydCB3cml0aW5nDQoNClJlYmEgU3VzYW4gSm9zZXBoICgyMEJEQTM3KSAtIEVEQSArIEFzc3VtcHRpb25zIG9mIExpbmVhciBSZWdyZXNzaW9uDQoNCkpheWFzcmVlIEMgICAgICAgICgyMEJEQTUzKSAtIEVEQSArIENvbXBpbGluZyBmb3IgdGhlIGZpbmFsIHJlcG9ydCArIFJlcG9ydCBXcml0aW5nDQoNCkFiaGlqaXRoIFAgSyAgICAgICgyMEJEQTYwKSAtIEVEQSArIFN0YXRpc3RpY2FsIFRlc3RzDQoNCkFuYW55YSBLdW1hcmkgICAgICgyMEJEQTY4KSAtIENvbXBpbGluZyBhbmQgZGVyaXZpbmcgaW5zaWdodHMgZnJvbSBFREEgKyBTVk0gKyBTdGVwd2lzZSBSZWdyZXNzaW9uICsgUmVwb3J0IFdyaXRpbmcNCg0KDQo8aDI+UmVmZXJlbmNlczo8L2gyPg0KDQoxLiBodHRwczovL21lZGl1bS5jb20vQGFyaXRyYWFkaGlrYXJpLmIzL3ByZWRpY3RpbmctY3VzdG9tZXItbGlmZXRpbWUtdmFsdWUtZm9yLWFuLWF1dG8taW5zdXJhbmNlLWNvbXBhbnktM2IyNGQ4YmY0ZTI0DQoNCjIuIGh0dHBzOi8vbWVkaWFhbHBoYS5jb20vYXJ0aWNsZS93aHktYXV0by1pbnN1cmFuY2UtYWR2ZXJ0aXNlcnMtc2hvdWxkLW9wdGltaXplLWZvci1jdXN0b21lci1saWZldGltZS12YWx1ZS8NCg0KMy4gaHR0cHM6Ly9naXRodWIuY29tL2FiaGl5ZXJhc2kvQ0xWLUF1dG8tSW5zdXJhbmNlL2Jsb2IvbWFzdGVyL0NvZGUvQ2x1c3RlcmluZy5SDQoNCjQuIGh0dHBzOi8vd3d3LmthZ2dsZS5jb20vZGt0YWxhaWNoYS9wcmVkaWN0LWN1c3RvbWVyLWxpZmUtdGltZS12YWx1ZS1jbHYjMS4tT2JqZWN0aXZlLTotLVByZWRpY3QtQ3VzdG9tZXItTGlmZS10aW1lLVZhbHVlLShDTFYtKWZvci1hbi1BdXRvLUluc3VyYW5jZS1Db21wYW55Lg0KDQo1LiBodHRwczovL3d3dy5rYWdnbGUuY29tL2p1YW5jYXJsb3N2ZW50b3NhL21vZGVscy10by1pbXByb3ZlLWN1c3RvbWVyLXJldGVudGlvbg0KDQo2LiBodHRwczovL3d3dy5rYWdnbGUuY29tL3ByYXRoaWJoYWtzL25vdGVib29rYWFhMzQwZWUzOC9lZGl0DQoNCg0KYGBge3J9DQpkYXRhPXN1YnNldChkYXRhLCBzZWxlY3QgPSAtYyhgRWZmZWN0aXZlIFRvIERhdGVgKSkNCmluc3RhbGwucGFja2FnZXMoIk1BU1MiKQ0KbGlicmFyeShNQVNTKQ0KDQpmdWxsLm1vZGVsIDwtIGxtKGBDdXN0b21lciBMaWZldGltZSBWYWx1ZWAgfi4sIGRhdGEgPSBkYXRhKQ0KIyBTdGVwd2lzZSByZWdyZXNzaW9uIG1vZGVsDQpzdGVwLm1vZGVsIDwtIHN0ZXBBSUMoZnVsbC5tb2RlbCwgZGlyZWN0aW9uID0gImJvdGgiLA0KICAgICAgICAgICAgICAgICAgICAgIHRyYWNlID0gRkFMU0UpDQpzdW1tYXJ5KHN0ZXAubW9kZWwpDQoNCmBgYA0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg==